lokesh341 commited on
Commit
621242c
·
verified ·
1 Parent(s): 4b5baa7

Update menu.py

Browse files
Files changed (1) hide show
  1. menu.py +178 -2031
menu.py CHANGED
@@ -1,2033 +1,180 @@
1
-
2
- <!DOCTYPE html>
3
- <html lang="en">
4
- <head>
5
- <meta charset="UTF-8">
6
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
- <title>Menu</title>
8
- <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/css/bootstrap.min.css" rel="stylesheet">
9
- <link href="https://cdn.jsdelivr.net/npm/bootstrap-icons/font/bootstrap-icons.css" rel="stylesheet">
10
- <link rel="preload" href="/static/placeholder.jpg" as="image">
11
- {% for section, items in ordered_menu.items() %}
12
- {% for item in items[:1] %}
13
- <link rel="preload" href="{{ item.Image1__c }}" as="image" fetchpriority="high">
14
- {% endfor %}
15
- {% endfor %}
16
- <style>
17
- body {
18
- font-family: Arial, sans-serif;
19
- background-color: #fdf4e3;
20
- margin: 0;
21
- padding: 0;
22
- display: flex;
23
- flex-direction: column;
24
- }
25
- .container {
26
- max-width: 900px;
27
- }
28
- .menu-card {
29
- max-width: 350px;
30
- border-radius: 15px;
31
- overflow: hidden;
32
- background-color: #fff;
33
- margin: auto;
34
- display: flex;
35
- flex-direction: column;
36
- opacity: 0;
37
- transition: opacity 0.3s ease-in-out;
38
- }
39
- .menu-card.visible {
40
- opacity: 1;
41
- }
42
-
43
- .video-container {
44
- position: relative;
45
- width: 100%;
46
- height: 200px;
47
- overflow: hidden;
48
- border-radius: 15px 15px 0 0;
49
- background-color: #000;
50
- touch-action: manipulation;
51
- }
52
-
53
- .video-container video {
54
- width: 100%;
55
- height: 100%;
56
- object-fit: cover;
57
- transition: opacity 0.5s ease;
58
- -webkit-user-select: none;
59
- user-select: none;
60
- }
61
-
62
- .video-container .video-placeholder {
63
- position: absolute;
64
- top: 0;
65
- left: 0;
66
- width: 100%;
67
- height: 100%;
68
- display: flex;
69
- align-items: center;
70
- justify-content: center;
71
- background-color: #f0f0f0;
72
- color: #666;
73
- font-size: 14px;
74
- }
75
-
76
- .video-container .play-button {
77
- position: absolute;
78
- top: 50%;
79
- left: 50%;
80
- transform: translate(-50%, -50%);
81
- width: 50px;
82
- height: 50px;
83
- background-color: rgba(0, 0, 0, 0.7);
84
- border-radius: 50%;
85
- display: flex;
86
- align-items: center;
87
- justify-content: center;
88
- cursor: pointer;
89
- z-index: 10;
90
- opacity: 0;
91
- transition: opacity 0.3s ease;
92
- }
93
-
94
- .video-container:hover .play-button {
95
- opacity: 1;
96
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
97
 
98
- .video-container .play-button i {
99
- color: white;
100
- font-size: 20px;
101
- margin-left: 3px;
102
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
103
 
104
- @media (max-width: 768px) {
105
- .video-container .play-button {
106
- opacity: 1;
107
- }
108
- .video-container:hover .play-button {
109
- opacity: 1;
110
- }
111
- .video-container {
112
- height: 150px;
113
- }
114
- }
115
-
116
- .card-title {
117
- font-size: 1.2rem;
118
- font-weight: bold;
119
- margin: 10px 0;
120
- }
121
- .card-text {
122
- font-size: 1rem;
123
- color: #6c757d;
124
- }
125
- .addbutton .btn {
126
- background-color: #28a745;
127
- color: white;
128
- padding: 10px 20px;
129
- font-size: 16px;
130
- font-weight: bold;
131
- border-radius: 5px;
132
- border: none;
133
- transition: background-color 0.3s ease;
134
- margin-left: 13px;
135
- }
136
- .addbutton .btn:hover {
137
- background-color: #218838;
138
- }
139
- .button-container {
140
- display: flex;
141
- flex-direction: column;
142
- align-items: center;
143
- justify-content: center;
144
- gap: 6px;
145
- }
146
- .customisable-text {
147
- color: #0FAA39;
148
- font-size: 10px;
149
- font-weight: 500;
150
- margin: 0;
151
- text-align: center;
152
- line-height: 1;
153
- }
154
- .btn-primary {
155
- font-size: 12px;
156
- font-weight: bold;
157
- border-radius: 8px;
158
- width: 70px;
159
- height: 35px;
160
- background-color: #0FAA39;
161
- border-color: #0FAA39;
162
- display: flex;
163
- align-items: center;
164
- justify-content: center;
165
- padding: 0;
166
- transition: background-color 0.3s ease, transform 0.1s ease;
167
- }
168
- .btn-primary:hover {
169
- background-color: #0D9232;
170
- border-color: #0D9232;
171
- transform: scale(1.05);
172
- }
173
- .btn-primary:active,
174
- .btn-primary:focus {
175
- background-color: #0B7A29;
176
- border-color: #0B7A29;
177
- box-shadow: none;
178
- transform: scale(0.98);
179
- }
180
- .view-cart-container {
181
- position: fixed;
182
- bottom: 20px;
183
- right: 20px;
184
- z-index: 999;
185
- display: flex;
186
- align-items: center;
187
- }
188
- .view-cart-button {
189
- background-color: #0FAA39;
190
- color: #fff;
191
- padding: 10px 20px;
192
- border-radius: 30px;
193
- font-size: 1rem;
194
- font-weight: bold;
195
- text-decoration: none;
196
- box-shadow: 0px 4px 8px rgba(0, 0, 0, 0.1);
197
- display: flex;
198
- align-items: center;
199
- justify-content: center;
200
- gap: 8px;
201
- transition: background-color 0.3s ease, transform 0.2s ease;
202
- }
203
- .view-cart-button:hover {
204
- background-color: #109835;
205
- text-decoration: none;
206
- color: #fff;
207
- transform: scale(1.05);
208
- }
209
- .cart-icon-badge {
210
- background-color: #fff;
211
- color: #0FAA39;
212
- border-radius: 50%;
213
- width: 24px;
214
- height: 24px;
215
- display: flex;
216
- align-items: center;
217
- justify-content: center;
218
- font-size: 12px;
219
- font-weight: bold;
220
- margin-left: 5px;
221
- transition: background-color 0.3s ease, color 0.3s ease;
222
- }
223
- .cart-icon-badge.active {
224
- background-color: #ffcc00;
225
- color: #0FAA39;
226
- }
227
- .avatar-dropdown-container {
228
- position: absolute;
229
- right: 10px;
230
- top: 50%;
231
- transform: translateY(-50%);
232
- display: flex;
233
- align-items: right;
234
- justify-content: center;
235
- }
236
- .avatar-icon {
237
- width: 40px;
238
- height: 40px;
239
- border-radius: 50%;
240
- background-color: #5bbfc1;
241
- cursor: pointer;
242
- display: flex;
243
- align-items: center;
244
- justify-content: center;
245
- color: white;
246
- font-size: 20px;
247
- font-weight: bold;
248
- }
249
- .dropdown-menu {
250
- position: absolute;
251
- right: 0;
252
- top: 100%;
253
- background-color: #fff;
254
- border-radius: 5px;
255
- width: 220px;
256
- box-shadow: 0px 4px 8px rgba(0, 0, 0, 0.1);
257
- display: none;
258
- }
259
- .dropdown-menu .dropdown-item {
260
- padding: 12px 16px;
261
- text-decoration: none;
262
- color: #333;
263
- border-bottom: 1px solid #ddd;
264
- display: block;
265
- font-size: 15px;
266
- }
267
- .dropdown-menu .dropdown-item:last-child {
268
- border-bottom: none;
269
- }
270
- .dropdown-menu .dropdown-item:hover {
271
- background-color: #f1f1f1;
272
- }
273
- .fixed-top-bar {
274
- position: relative;
275
- top: 0;
276
- left: 0;
277
- width: 100%;
278
- height: 54px;
279
- background: linear-gradient(45deg, #FFA07A, #FFB347);
280
- color: white;
281
- padding: 15px;
282
- display: flex;
283
- justify-content: space-between;
284
- align-items: center;
285
- z-index: 1000;
286
- }
287
- .search-bar-container {
288
- position: absolute;
289
- left: 20px;
290
- top: 50%;
291
- transform: translateY(-50%);
292
- display: flex;
293
- align-items: center;
294
- width: 300px;
295
- max-width: 90%;
296
- }
297
- .search-bar-container input {
298
- width: 100%;
299
- padding: 8px 15px 8px 40px;
300
- font-size: 16px;
301
- border-radius: 25px;
302
- border: none;
303
- background-color: #fff;
304
- box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
305
- outline: none;
306
- }
307
- .search-bar-container input::placeholder {
308
- color: #888;
309
- }
310
- .search-icon {
311
- position: absolute;
312
- left: 15px;
313
- font-size: 18px;
314
- color: #888;
315
- }
316
- .autocomplete-suggestions {
317
- position: absolute;
318
- top: 100%;
319
- left: 0;
320
- width: 100%;
321
- max-height: 200px;
322
- overflow-y: auto;
323
- background-color: #fff;
324
- border: 1px solid #ddd;
325
- border-radius: 5px;
326
- box-shadow: 0px 4px 8px rgba(0, 0, 0, 0.1);
327
- z-index: 1000;
328
- display: none;
329
- }
330
- .autocomplete-suggestions .suggestion-item {
331
- padding: 8px 15px;
332
- cursor: pointer;
333
- font-size: 14px;
334
- color: #333;
335
- }
336
- .autocomplete-suggestions .suggestion-item:hover {
337
- background-color: #f1f1f1;
338
- }
339
- .addon-section {
340
- background-color: #fff;
341
- border: 2px solid #6c757d;
342
- border-radius: 8px;
343
- padding: 12px;
344
- margin-bottom: 10px;
345
- }
346
- .addon-section h6 {
347
- margin-bottom: 10px;
348
- font-size: 1.1rem;
349
- font-weight: bold;
350
- color: #343a40;
351
- }
352
- .addon-section .form-check {
353
- display: inline-flex;
354
- align-items: center;
355
- margin-left: 10px;
356
- color: #343a40;
357
- }
358
- .addon-section .form-check-input {
359
- -webkit-appearance: none;
360
- -moz-appearance: none;
361
- appearance: none;
362
- width: 20px;
363
- height: 20px;
364
- border: 2px solid #343a40;
365
- border-radius: 5px;
366
- background-color: #f0f0f0;
367
- position: relative;
368
- margin-right: 10px;
369
- }
370
- .addon-section .form-check-input:checked {
371
- background-color: #006400;
372
- border-color: #006400;
373
- }
374
- .addon-section .form-check-input:checked::before {
375
- content: '\2713';
376
- font-size: 14px;
377
- position: absolute;
378
- top: 3px;
379
- left: 4px;
380
- color: white;
381
- }
382
- .addon-section .form-check-label {
383
- font-size: 16px;
384
- margin-left: 5px;
385
- margin-right: 15px;
386
- cursor: pointer;
387
- display: inline-block;
388
- vertical-align: middle;
389
- }
390
- form.text-center.mb-4 {
391
- display: flex;
392
- flex-direction: column;
393
- align-items: center;
394
- justify-content: center;
395
- margin-bottom: 5px;
396
- }
397
- .modal-header {
398
- padding: 10px 15px;
399
- }
400
- .modal-title {
401
- font-size: 16px;
402
- font-weight: bold;
403
- }
404
- .modal-body {
405
- max-height: 60vh;
406
- overflow-y: auto;
407
- padding: 15px;
408
- }
409
- .modal-body #modal-img {
410
- max-height: 200px;
411
- width: 100%;
412
- object-fit: cover;
413
- border-radius: 8px;
414
- margin-bottom: 10px;
415
- }
416
- .modal-body #modal-name {
417
- font-size: 20px;
418
- font-weight: bold;
419
- text-align: center;
420
- margin-bottom: 5px;
421
- }
422
- .modal-body #modal-price {
423
- font-size: 16px;
424
- font-weight: bold;
425
- color: #6c757d;
426
- text-align: center;
427
- margin-bottom: 10px;
428
- }
429
- .modal-body #modal-description {
430
- font-size: 14px;
431
- color: #6c757d;
432
- margin-bottom: 10px;
433
- }
434
- .modal-body .nutritional-info {
435
- font-size: 12px;
436
- color: #6c757d;
437
- margin-bottom: 10px;
438
- }
439
- .modal-body #modal-addons h6,
440
- .modal-body #first-row h6 {
441
- font-size: 14px;
442
- font-weight: bold;
443
- margin-bottom: 10px;
444
- }
445
- .modal-footer {
446
- display: flex;
447
- align-items: center;
448
- justify-content: space-between;
449
- padding: 10px;
450
- }
451
- .modal-footer .d-flex {
452
- display: flex;
453
- align-items: center;
454
- gap: 10px;
455
- }
456
- .modal-footer .btn {
457
- height: 40px;
458
- padding: 0 15px;
459
- }
460
- .modal-footer .form-control {
461
- width: 50px;
462
- height: 40px;
463
- text-align: center;
464
- }
465
- .modal-footer .btn-primary {
466
- background-color: #0FAA39;
467
- border-color: #0FAA39;
468
- font-weight: bold;
469
- padding: 10px 20px;
470
- height: 40px;
471
- display: flex;
472
- justify-content: center;
473
- align-items: center;
474
- width: auto;
475
- }
476
- .modal-footer .btn-outline-secondary {
477
- height: 40px;
478
- width: 40px;
479
- }
480
- .item-details {
481
- background-color: #f8f9fa;
482
- border-radius: 8px;
483
- padding: 10px;
484
- margin: 10px 15px;
485
- display: none;
486
- }
487
- .item-details.show {
488
- display: block;
489
- }
490
- .item-details h6 {
491
- font-size: 0.9rem;
492
- font-weight: bold;
493
- margin-bottom: 5px;
494
- }
495
- .item-details p {
496
- font-size: 0.85rem;
497
- margin-bottom: 5px;
498
- color: #333;
499
- }
500
- .item-details .nutritional-info {
501
- display: grid;
502
- grid-template-columns: 1fr 1fr;
503
- gap: 10px;
504
- background-color: #e9ecef;
505
- padding: 10px;
506
- border-radius: 5px;
507
- font-size: 0.85rem;
508
- line-height: 1.5;
509
- }
510
- .toggle-details {
511
- cursor: pointer;
512
- color: #0FAA39;
513
- font-size: 0.9rem;
514
- margin-left: 15px;
515
- margin-bottom: 10px;
516
- display: inline-block;
517
- }
518
- .toggle-details:hover {
519
- text-decoration: underline;
520
- }
521
- .category-buttons {
522
- display: flex;
523
- flex-wrap: wrap;
524
- gap: 10px;
525
- justify-content: center;
526
- margin-top: 10px;
527
- }
528
- .category-button {
529
- background-color: #fff;
530
- border: 2px solid #0FAA39;
531
- color: #0FAA39;
532
- padding: 5px 15px;
533
- border-radius: 20px;
534
- font-size: 0.9rem;
535
- cursor: pointer;
536
- transition: background-color 0.3s, color 0.3s;
537
- }
538
- .category-button.selected {
539
- background-color: #0FAA39;
540
- color: #fff;
541
- border-color: #0FAA39;
542
- }
543
- .category-button:hover {
544
- background-color: #e6f4ea;
545
- }
546
- .quantity-selector {
547
- display: flex;
548
- align-items: center;
549
- gap: 5px;
550
- }
551
- .quantity-selector .btn {
552
- width: 25px;
553
- height: 25px;
554
- padding: 0;
555
- font-size: 12px;
556
- line-height: 25px;
557
- text-align: center;
558
- }
559
- .quantity-selector .quantity-display {
560
- width: 25px;
561
- text-align: center;
562
- font-size: 12px;
563
- font-weight: bold;
564
- line-height: 25px;
565
- }
566
- .quantity-selector .quantity-to-add,
567
- .quantity-selector .quantity-to-remove {
568
- width: 45px;
569
- height: 25px;
570
- font-size: 12px;
571
- padding: 0 5px;
572
- }
573
- .modal-dialog {
574
- max-height: 90vh;
575
- }
576
- .modal-body::-webkit-scrollbar {
577
- width: 8px;
578
- }
579
- .modal-body::-webkit-scrollbar-track {
580
- background: #f1f1f1;
581
- border-radius: 10px;
582
- }
583
- .modal-body::-webkit-scrollbar-thumb {
584
- background: #0FAA39;
585
- border-radius: 10px;
586
- }
587
- .modal-body::-webkit-scrollbar-thumb:hover {
588
- background: #0D9232;
589
- }
590
- @media (max-width: 576px) {
591
- .fixed-top-bar {
592
- height: 60px;
593
- padding: 10px;
594
- }
595
- .search-bar-container {
596
- width: 80%;
597
- max-width: 100%;
598
- left: 10px;
599
- top: 50%;
600
- transform: translateY(-50%);
601
- }
602
- .search-bar-container input {
603
- padding: 6px 12px 6px 35px;
604
- font-size: 14px;
605
- border-radius: 20px;
606
- }
607
- .search-icon {
608
- left: 12px;
609
- font-size: 16px;
610
- }
611
- .avatar-dropdown-container {
612
- right: 10px;
613
- }
614
- .avatar-icon {
615
- width: 40px;
616
- height: 40px;
617
- font-size: 20px;
618
- }
619
- .dropdown-menu {
620
- width: 220px;
621
- }
622
- .dropdown-menu .dropdown-item {
623
- padding: 12px 16px;
624
- font-size: 15px;
625
- }
626
- .category-buttons {
627
- gap: 8px;
628
- }
629
- .category-button {
630
- padding: 4px 12px;
631
- font-size: 0.85rem;
632
- }
633
- .modal-dialog {
634
- max-width: 96%;
635
- margin: 5px auto;
636
- }
637
- .modal-header {
638
- padding: 5px 10px;
639
- }
640
- .modal-title {
641
- font-size: 14px;
642
- }
643
- .modal-body {
644
- max-height: 50vh;
645
- padding: 8px;
646
- }
647
- .modal-body #modal-img {
648
- max-height: 150px;
649
- width: 100%;
650
- max-width: 150px;
651
- margin: 0 auto 5px;
652
- display: block;
653
- }
654
- .modal-body #modal-name {
655
- font-size: 18px;
656
- margin-bottom: 3px;
657
- }
658
- .modal-body #modal-price {
659
- font-size: 14px;
660
- margin-bottom: 5px;
661
- }
662
- .modal-body #modal-description {
663
- font-size: 12px;
664
- margin-bottom: 5px;
665
- }
666
- .modal-body .nutritional-info {
667
- font-size: 10px;
668
- margin-bottom: 5px;
669
- }
670
- .modal-body #modal-addons h6,
671
- .modal-body #first-row h6 {
672
- font-size: 12px;
673
- margin-bottom: 5px;
674
- }
675
- .modal-footer {
676
- padding: 5px;
677
- }
678
- .modal-footer .btn {
679
- height: 30px;
680
- padding: 0 10px;
681
- }
682
- .modal-footer .form-control {
683
- width: 30px;
684
- height: 30px;
685
- font-size: 12px;
686
- font-weight: bold;
687
- }
688
- .modal-footer .btn-outline-secondary {
689
- width: 25px;
690
- height: 25px;
691
- font-size: 12px;
692
- line-height: 25px;
693
- }
694
- .modal-footer .btn-primary {
695
- font-size: 12px;
696
- height: 30px;
697
- padding: 0 15px;
698
- border-radius: 5px;
699
- }
700
- .btn-primary {
701
- font-size: 10px;
702
- width: 50px;
703
- height: 25px;
704
- }
705
- .customisable-text {
706
- font-size: 8px;
707
- }
708
- .button-container {
709
- gap: 3px;
710
- }
711
- .quantity-selector .btn {
712
- width: 18px;
713
- height: 18px;
714
- font-size: 9px;
715
- line-height: 18px;
716
- }
717
- .quantity-selector .quantity-display {
718
- width: 18px;
719
- font-size: 9px;
720
- line-height: 18px;
721
- }
722
- .quantity-selector .quantity-to-add,
723
- .quantity-selector .quantity-to-remove {
724
- width: 35px;
725
- height: 18px;
726
- font-size: 9px;
727
- }
728
- .view-cart-button {
729
- padding: 8px 16px;
730
- font-size: 14px;
731
- }
732
- .cart-icon-badge {
733
- width: 20px;
734
- height: 20px;
735
- font-size: 10px;
736
- }
737
- }
738
- </style>
739
- </head>
740
- <body>
741
-
742
- <div class="fixed-top-bar">
743
- <div class="avatar-dropdown-container">
744
- <div class="avatar-icon">
745
- <span>{{ first_letter }}</span>
746
- </div>
747
- <div class="dropdown-menu">
748
- <a href="{{ url_for('user_details.customer_details') }}" class="dropdown-item">View Profile</a>
749
- <a href="{{ url_for('orderhistory.order_history') }}" class="dropdown-item">Order History</a>
750
- <a href="{{ url_for('logout') }}" class="dropdown-item">Logout</a>
751
- </div>
752
- </div>
753
- <div class="search-bar-container">
754
- <input type="text" id="searchBar" class="form-control" placeholder="Search items or sections..." autocomplete="off">
755
- <i class="bi bi-search search-icon"></i>
756
- <div id="autocompleteSuggestions" class="autocomplete-suggestions"></div>
757
- </div>
758
- </div>
759
-
760
- <form method="get" action="/menu" class="text-center mb-4" id="categoryForm">
761
- <label class="form-label fw-bold">Select a Category:</label>
762
- <div class="category-buttons">
763
- {% for category in categories %}
764
- <button type="button" class="category-button {% if selected_category == category %}selected{% endif %}" data-category="{{ category }}">{{ category }}</button>
765
- {% endfor %}
766
- <button type="button" class="category-button {% if selected_category == 'Customized Dish' %}selected{% endif %}" data-category="Customized Dish">Customized Dish</button>
767
- </div>
768
- <input type="hidden" name="category" id="selectedCategoryInput" value="{{ selected_category }}">
769
- </form>
770
-
771
- <div class="container mt-4">
772
- {% if selected_category == "Customized Dish" %}
773
- <div id="custom-dish-form" class="mt-4">
774
- <h3>Create Your Custom Dish</h3>
775
- <form method="POST" action="/customdish/generate_custom_dish">
776
- <div class="mb-3">
777
- <label for="custom-dish-name" class="form-label">Dish Name</label>
778
- <input type="text" class="form-control" id="custom-dish-name" name="name" required>
779
- </div>
780
- <div class="mb-3 position-relative">
781
- <label for="custom-dish-description" class="form-label">Dish Description</label>
782
- <textarea class="form-control" id="custom-dish-description" name="description" required></textarea>
783
- <div id="descriptionSuggestions" class="autocomplete-suggestions"></div>
784
- </div>
785
- <button type="submit" class="btn btn-primary">Submit</button>
786
- </form>
787
- </div>
788
- {% else %}
789
- {% for section, items in ordered_menu.items() %}
790
- <h3>{{ section }}</h3>
791
- <div class="row">
792
- {% for item in items %}
793
- <div class="col-md-6 mb-4">
794
- <div class="card menu-card">
795
- <div class="video-container">
796
- <video
797
- id="video-{{ loop.index }}"
798
- preload="metadata"
799
- poster="{{ item.Image1__c if item.Image1__c else '/static/placeholder.jpg' }}"
800
- data-src="{{ item.Video1__c }}"
801
- muted
802
- loop
803
- >
804
- <source src="{{ item.Video1__c }}" type="video/mp4">
805
- Your browser does not support the video tag.
806
- </video>
807
- {% if not item.Video1__c %}
808
- <div class="video-placeholder">
809
- No video available
810
- </div>
811
- {% endif %}
812
- <div class="play-button" onclick="togglePlay(this)">
813
- <i class="bi bi-play-fill"></i>
814
- </div>
815
- </div>
816
-
817
- <div class="addbutton">
818
- <div class="card-body d-flex align-items-center justify-content-between">
819
- <div>
820
- <h5 class="card-title">{{ item.Name }}</h5>
821
- <p class="card-text">${{ item.Price__c }}</p>
822
- </div>
823
- <div class="d-flex flex-column align-item-center justify-content-center">
824
- <div class="button-container" data-item-name="{{ item.Name }}" data-item-price="{{ item.Price__c }}" data-item-image="{{ item.Image1__c }}" data-item-section="{{ item.Section__c }}" data-item-category="{{ selected_category }}">
825
- {% if item.Section__c == 'Soft Drinks' %}
826
- <button class="btn btn-primary add-to-cart-btn" onclick="handleSoftDrinkAdd(this)">ADD</button>
827
- <div class="quantity-selector" style="display: none;">
828
- <button class="btn btn-outline-secondary decrease-btn" onclick="decreaseQuantity(this)">-</button>
829
- <select class="quantity-to-remove">
830
- {% for i in range(1, 21) %}
831
- <option value="{{ i }}">{{ i }}</option>
832
- {% endfor %}
833
- </select>
834
- <span class="quantity-display">0</span>
835
- <button class="btn btn-outline-secondary increase-btn" onclick="increaseQuantity(this)">+</button>
836
- <select class="quantity-to-add">
837
- {% for i in range(1, 21) %}
838
- <option value="{{ i }}">{{ i }}</option>
839
- {% endfor %}
840
- </select>
841
- </div>
842
- {% else %}
843
- <button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#itemModal"
844
- onclick="showItemDetails('{{ item.Name }}', '{{ item.Price__c }}', '{{ item.Image2__c }}', '{{ item.Description__c }}', '{{ item.Section__c }}','{{ selected_category }}')">
845
- ADD
846
- </button>
847
- {% endif %}
848
- {% if item.Section__c != 'Apetizer' and item.Section__c != 'Customized dish' and item.Section__c !='Soft Drinks' %}
849
- <span class="customisable-text">Customisable</span>
850
- {% endif %}
851
- </div>
852
- </div>
853
- </div>
854
- </div>
855
- <div class="toggle-details" data-item-name="{{ item.Name }}">Show Details</div>
856
- <div class="item-details" id="details-{{ item.Name | replace(' ', '-') }}"></div>
857
- </div>
858
- </div>
859
- {% endfor %}
860
- </div>
861
- {% endfor %}
862
- {% endif %}
863
- </div>
864
-
865
- <div class="view-cart-container">
866
- <a href="{{ url_for('cart.cart') }}" class="view-cart-button">
867
- <i class="bi bi-cart"></i>
868
- view Cart
869
- <span id="cart-item-count" class="cart-icon-badge {% if cart_item_count > 0 %}active{% endif %}">
870
- {{ cart_item_count if cart_item_count > 0 else '' }}
871
- </span>
872
- </a>
873
- </div>
874
-
875
- <div class="modal fade" id="itemModal" tabindex="-1" aria-labelledby="itemModalLabel" aria-hidden="true">
876
- <div class="modal-dialog modal-dialog-centered">
877
- <div class="modal-content">
878
- <div class="modal-header">
879
- <h5 class="modal-title" id="itemModalLabel">Item Details</h5>
880
- <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
881
- </div>
882
- <div class="modal-body">
883
- <img id="modal-img" class="img-fluid rounded mb-3 d-block mx-auto" alt="Item Image">
884
- <h5 id="modal-name" class="fw-bold text-center"></h5>
885
- <p id="modal-price" class="text-muted text-center"></p>
886
- <p id="modal-description" class="text-secondary"></p>
887
- <p class="nutritional-info" id="modal-nutritional-info"></p>
888
-
889
- <div id="first-row">
890
- <h6 id="first-row-title" style="display: none;">Customization Options</h6>
891
- <div class="addon-section">
892
- <h6>Choose Preparation Style</h6>
893
- <div id="prep-style-options"></div>
894
- </div>
895
- <div class="addon-section">
896
- <h6>Type</h6>
897
- <div id="type-options"></div>
898
- </div>
899
- <div class="addon-section">
900
- <h6>Spice Level</h6>
901
- <div id="spice-level-options"></div>
902
- </div>
903
- </div>
904
-
905
- <div id="modal-addons" class="modal-addons mt-4">
906
- <h6 id="addons-title">Customization Options</h6>
907
- <div id="addons-list" class="addons-container">Loading customization options...</div>
908
- </div>
909
-
910
- <div class="mt-4">
911
- <h6>Custom Request</h6>
912
- <textarea id="modal-instructions" class="form-control" placeholder="Enter any special instructions here..."></textarea>
913
- </div>
914
- <span id="modal-section" data-section="" data-category="" style="display: none;"></span>
915
- </div>
916
- <div class="modal-footer d-flex align-items-center justify-content-between">
917
- <div class="d-flex align-items-center gap-2">
918
- <button type="button" class="btn btn-outline-secondary" id="decreaseQuantity">-</button>
919
- <input type="text" class="form-control text-center" id="quantityInput" value="1" readonly style="width: 50px;"/>
920
- <button type="button" class="btn btn-outline-secondary" id="increaseQuantity">+</button>
921
- </div>
922
- <button type="button" class="btn btn-primary" onclick="addToCartFromModal()">Add to Cart</button>
923
- </div>
924
- </div>
925
- </div>
926
- </div>
927
-
928
- <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/js/bootstrap.bundle.min.js"></script>
929
- <script>
930
- function togglePlay(button) {
931
- const videoContainer = button.closest('.video-container');
932
- const video = videoContainer.querySelector('video');
933
-
934
- if (video.paused) {
935
- if (!video.src && video.dataset.src) {
936
- video.src = video.dataset.src;
937
- }
938
- video.play()
939
- .then(() => {
940
- button.innerHTML = '<i class="bi bi-pause-fill"></i>';
941
- })
942
- .catch(e => console.log('Video play error:', e));
943
- } else {
944
- video.pause();
945
- button.innerHTML = '<i class="bi bi-play-fill"></i>';
946
- }
947
- }
948
-
949
- const menuItems = [
950
- {% for section, items in ordered_menu.items() %}
951
- {% for item in items %}
952
- "{{ item.Name }}",
953
- {% endfor %}
954
- {% endfor %}
955
- ];
956
-
957
- const ingredientsList = [
958
- "Basmati Rice", "Bell Pepper", "Biryani Masala", "Butter", "Capsicum", "Cauliflower",
959
- "Chickpea Flour (Besan)", "Chickpea Flour (for batter)", "Chickpeas (Channa)", "Chili Powder",
960
- "Chili Sauce", "Coconut Milk", "Coriander Powder", "Cornflour", "Cream", "Cumin Powder",
961
- "Cumin Seeds", "Curd (Yogurt)", "Curry Leaves", "Fish (e.g., King Fish or Salmon)",
962
- "Fresh Coriander Leaves", "Garam Masala", "Garlic", "Ghee (Clarified Butter)", "Ginger",
963
- "Ginger-Garlic Paste", "Goat Meat (Mutton)", "Green Chilies", "Honey",
964
- "Kasuri Methi (dried fenugreek leaves)", "Lemon Juice", "Mango Puree", "Mint Leaves",
965
- "Mixed Vegetables (Carrot, Peas, Potato, Cauliflower)", "Mixed Vegetables (Carrot, Peas, Potato)",
966
- "Mustard Seeds", "Mutton (Goat Meat)", "Oil", "Oil (for frying)", "Onion",
967
- "Paneer (Indian Cottage Cheese)", "Peas", "Potatoes", "Prawns", "Red Chili Powder",
968
- "Rice Flour", "Saffron", "Salt", "Soy Sauce", "Spring Onion", "Tamarind (for sourness)",
969
- "Tomato Ketchup", "Tomatoes", "Turmeric Powder", "Vinegar", "Water", "Wheat Flour (for dough)",
970
- "Whole Wheat Flour", "Yogurt (Curd)"
971
- ];
972
-
973
- const menuItemDetails = {
974
- "Veg Manchurian": {
975
- ingredients: "Cauliflower, Onion, Ginger, Garlic, Soy Sauce, Cornflour, Green Chilies, Capsicum, Spring Onion",
976
- nutritionalInfo: { calories: 250, protein: 5, carbs: 35, fats: 12, fiber: 3, sugar: 2 },
977
- allergens: "Soy, Gluten"
978
- },
979
- "Veg Biryani": {
980
- ingredients: "Basmati Rice, Mixed Vegetables (Carrot, Peas, Potato, Cauliflower), Ginger, Garlic, Biryani Masala, Mint Leaves, Curd, Onion, Ghee",
981
- nutritionalInfo: { calories: 300, protein: 6, carbs: 50, fats: 12, fiber: 5, sugar: 3 },
982
- allergens: "Dairy"
983
- },
984
- "Sukka Gosht (Goat)": {
985
- ingredients: "Goat Meat, Ginger-Garlic Paste, Green Chilies, Onion, Tomatoes, Garam Masala, Coriander Powder, Cumin Powder, Fresh Coriander",
986
- nutritionalInfo: { calories: 450, protein: 35, carbs: 10, fats: 30, fiber: 2, sugar: 1 },
987
- allergens: "None"
988
- },
989
- "Samosa": {
990
- ingredients: "Potatoes, Peas, Onion, Ginger, Cumin Seeds, Garam Masala, Wheat Flour (for dough), Oil (for frying)",
991
- nutritionalInfo: { calories: 150, protein: 3, carbs: 25, fats: 7, fiber: 2, sugar: 1 },
992
- allergens: "Gluten"
993
- },
994
- "Roti": {
995
- ingredients: "Whole Wheat Flour, Water, Salt",
996
- nutritionalInfo: { calories: 150, protein: 4, carbs: 30, fats: 1, fiber: 3, sugar: 0 },
997
- allergens: "Gluten"
998
- },
999
- "Prawn Fry": {
1000
- ingredients: "Prawns, Garlic, Ginger, Chili Powder, Coriander Powder, Cumin Powder, Lemon Juice, Oil",
1001
- nutritionalInfo: { calories: 350, protein: 25, carbs: 10, fats: 20, fiber: 1, sugar: 1 },
1002
- allergens: "Shellfish"
1003
- },
1004
- "Paneer Butter Masala": {
1005
- ingredients: "Paneer, Butter, Cream, Tomato Puree, Onion, Ginger, Garlic, Garam Masala",
1006
- nutritionalInfo: { calories: 400, protein: 15, carbs: 20, fats: 25, fiber: 2, sugar: 3 },
1007
- allergens: "Dairy"
1008
- },
1009
- "Paneer Biryani": {
1010
- ingredients: "Paneer, Basmati Rice, Mixed Vegetables (Carrot, Peas, Potato), Onion, Tomatoes, Biryani Masala, Mint Leaves, Curd",
1011
- nutritionalInfo: { calories: 350, protein: 12, carbs: 55, fats: 15, fiber: 4, sugar: 3 },
1012
- allergens: "Dairy"
1013
- },
1014
- "Onion Pakoda": {
1015
- ingredients: "Onion, Chickpea Flour (Besan), Rice Flour, Green Chilies, Cumin Seeds, Ginger, Turmeric Powder, Oil (for frying)",
1016
- nutritionalInfo: { calories: 200, protein: 5, carbs: 30, fats: 8, fiber: 3, sugar: 2 },
1017
- allergens: "Gluten (if cross-contamination)"
1018
- },
1019
- "Mutton Biryani": {
1020
- ingredients: "Mutton, Basmati Rice, Onion, Tomatoes, Ginger-Garlic Paste, Biryani Masala, Mint Leaves, Yogurt, Ghee",
1021
- nutritionalInfo: { calories: 500, protein: 30, carbs: 50, fats: 25, fiber: 4, sugar: 3 },
1022
- allergens: "Dairy"
1023
- },
1024
- "Fish Curry": {
1025
- ingredients: "Fish (any firm fish like Salmon or King Fish), Onion, Tomato, Ginger-Garlic Paste, Curry Leaves, Coconut Milk, Tamarind, Mustard Seeds",
1026
- nutritionalInfo: { calories: 300, protein: 25, carbs: 10, fats: 20, fiber: 2, sugar: 1 },
1027
- allergens: "Fish"
1028
- },
1029
- "Fiery Mango Glaze Chicken": {
1030
- ingredients: "Chicken, Mango Puree, Chili Sauce, Soy Sauce, Honey, Garlic, Ginger, Lemon Juice",
1031
- nutritionalInfo: { calories: 350, protein: 30, carbs: 15, fats: 18, fiber: 1, sugar: 5 },
1032
- allergens: "Soy"
1033
- },
1034
- "Chilli Gobi": {
1035
- ingredients: "Cauliflower, Onion, Green Chilies, Soy Sauce, Cornflour, Garlic, Ginger, Cumin Powder",
1036
- nutritionalInfo: { calories: 250, protein: 6, carbs: 35, fats: 12, fiber: 3, sugar: 2 },
1037
- allergens: "Soy, Gluten"
1038
- },
1039
- "Chilli Chicken": {
1040
- ingredients: "Chicken, Bell Pepper, Onion, Green Chilies, Soy Sauce, Cornflour, Garlic, Ginger",
1041
- nutritionalInfo: { calories: 400, protein: 35, carbs: 20, fats: 18, fiber: 2, sugar: 2 },
1042
- allergens: "Soy, Gluten"
1043
- },
1044
- "Chicken Manchurian": {
1045
- ingredients: "Chicken, Onion, Garlic, Ginger, Soy Sauce, Cornflour, Green Chilies, Capsicum",
1046
- nutritionalInfo: { calories: 350, protein: 25, carbs: 20, fats: 18, fiber: 2, sugar: 2 },
1047
- allergens: "Soy, Gluten"
1048
- },
1049
- "Chicken Curry": {
1050
- ingredients: "Chicken, Onion, Tomatoes, Ginger-Garlic Paste, Garam Masala, Coconut Milk, Coriander Leaves",
1051
- nutritionalInfo: { calories: 350, protein: 28, carbs: 15, fats: 12, fiber: 4, sugar: 2 },
1052
- allergens: "None"
1053
- },
1054
- "Chicken Biryani": {
1055
- ingredients: "Chicken, Basmati Rice, Onion, Tomatoes, Ginger-Garlic Paste, Biryani Masala, Mint Leaves, Curd",
1056
- nutritionalInfo: { calories: 500, protein: 35, carbs: 60, fats: 20, fiber: 5, sugar: 3 },
1057
- allergens: "Dairy"
1058
- },
1059
- "Channa Masala": {
1060
- ingredients: "Chickpeas, Onion, Tomatoes, Ginger-Garlic Paste, Garam Masala, Coriander Powder, Cumin Seeds, Lemon Juice",
1061
- nutritionalInfo: { calories: 250, protein: 10, carbs: 45, fats: 5, fiber: 6, sugar: 2 },
1062
- allergens: "None"
1063
- }
1064
- };
1065
-
1066
- function addToCartLocalStorage(payload) {
1067
- let cart = JSON.parse(localStorage.getItem('cart')) || [];
1068
- const existingItem = cart.find(item =>
1069
- item.itemName === payload.itemName &&
1070
- item.instructions === payload.instructions &&
1071
- JSON.stringify(item.addons) === JSON.stringify(payload.addons)
1072
- );
1073
- if (existingItem) {
1074
- existingItem.quantity += payload.quantity;
1075
- } else {
1076
- cart.push(payload);
1077
- }
1078
- localStorage.setItem('cart', JSON.stringify(cart));
1079
- return cart;
1080
- }
1081
-
1082
- function removeFromCartLocalStorage(itemName, quantityToRemove, instructions, addons) {
1083
- let cart = JSON.parse(localStorage.getItem('cart')) || [];
1084
- const itemIndex = cart.findIndex(item =>
1085
- item.itemName === itemName &&
1086
- item.instructions === instructions &&
1087
- JSON.stringify(item.addons) === JSON.stringify(addons)
1088
- );
1089
- if (itemIndex !== -1) {
1090
- if (quantityToRemove >= cart[itemIndex].quantity) {
1091
- cart.splice(itemIndex, 1);
1092
- } else {
1093
- cart[itemIndex].quantity -= quantityToRemove;
1094
- }
1095
- }
1096
- localStorage.setItem('cart', JSON.stringify(cart));
1097
- return cart;
1098
- }
1099
-
1100
- function getCartLocalStorage() {
1101
- return JSON.parse(localStorage.getItem('cart')) || [];
1102
- }
1103
-
1104
- document.addEventListener('DOMContentLoaded', function () {
1105
- const videoContainers = document.querySelectorAll('.video-container');
1106
-
1107
- videoContainers.forEach(container => {
1108
- const video = container.querySelector('video');
1109
- const playButton = container.querySelector('.play-button');
1110
-
1111
- const observer = new IntersectionObserver((entries) => {
1112
- entries.forEach(entry => {
1113
- if (entry.isIntersecting && video.dataset.src && !video.src) {
1114
- video.src = video.dataset.src;
1115
- observer.unobserve(entry.target);
1116
- }
1117
- });
1118
- }, {
1119
- rootMargin: '200px',
1120
- threshold: 0.1
1121
- });
1122
-
1123
- observer.observe(container);
1124
-
1125
- container.addEventListener('mouseenter', () => {
1126
- if (window.innerWidth > 768) {
1127
- if (video.src || video.dataset.src) {
1128
- if (!video.src) video.src = video.dataset.src;
1129
- video.play().catch(e => console.log('Autoplay prevented:', e));
1130
- }
1131
- }
1132
- });
1133
-
1134
- container.addEventListener('mouseleave', () => {
1135
- if (window.innerWidth > 768) {
1136
- if (!video.paused) {
1137
- video.pause();
1138
- if (playButton) {
1139
- playButton.innerHTML = '<i class="bi bi-play-fill"></i>';
1140
- }
1141
- }
1142
- }
1143
- });
1144
-
1145
- container.addEventListener('touchstart', (e) => {
1146
- e.preventDefault();
1147
- if (window.innerWidth <= 768) {
1148
- if (video.src || video.dataset.src) {
1149
- if (!video.src) video.src = video.dataset.src;
1150
- if (video.paused) {
1151
- video.play()
1152
- .then(() => {
1153
- if (playButton) {
1154
- playButton.innerHTML = '<i class="bi bi-pause-fill"></i>';
1155
- }
1156
- })
1157
- .catch(e => console.log('Video play error:', e));
1158
- } else {
1159
- video.pause();
1160
- if (playButton) {
1161
- playButton.innerHTML = '<i class="bi bi-play-fill"></i>';
1162
- }
1163
- }
1164
- }
1165
- }
1166
- }, { passive: false });
1167
- });
1168
-
1169
- const avatarContainer = document.querySelector('.avatar-dropdown-container');
1170
- const dropdownMenu = document.querySelector('.dropdown-menu');
1171
-
1172
- avatarContainer.addEventListener('click', function (event) {
1173
- event.stopPropagation();
1174
- dropdownMenu.style.display = dropdownMenu.style.display === 'block' ? 'none' : 'block';
1175
- });
1176
-
1177
- document.addEventListener('click', function (event) {
1178
- if (!avatarContainer.contains(event.target)) {
1179
- dropdownMenu.style.display = 'none';
1180
- }
1181
- });
1182
-
1183
- const dropdownItems = document.querySelectorAll('.dropdown-item');
1184
- dropdownItems.forEach(item => {
1185
- item.addEventListener('click', function () {
1186
- dropdownMenu.style.display = 'none';
1187
- });
1188
- });
1189
-
1190
- const menuCards = document.querySelectorAll('.menu-card');
1191
- menuCards.forEach(card => {
1192
- const itemName = card.querySelector('.card-title').innerText;
1193
- const detailsDiv = card.querySelector('.item-details');
1194
- const detailsData = menuItemDetails[itemName];
1195
-
1196
- if (detailsData) {
1197
- detailsDiv.innerHTML = `
1198
- <h6>Ingredients</h6>
1199
- <p>${detailsData.ingredients}</p>
1200
- <h6>Nutritional Info</h6>
1201
- <div class="nutritional-info">
1202
- <span>Calories: ${detailsData.nutritionalInfo.calories} kcal</span>
1203
- <span>Carbs: ${detailsData.nutritionalInfo.carbs}g</span>
1204
- <span>Protein: ${detailsData.nutritionalInfo.protein}g</span>
1205
- <span>Fats: ${detailsData.nutritionalInfo.fats}g</span>
1206
- </div>
1207
- <h6>Allergens</h6>
1208
- <p>${detailsData.allergens}</p>
1209
- `;
1210
- } else {
1211
- detailsDiv.innerHTML = '<p>No details available for this item.</p>';
1212
- }
1213
- });
1214
-
1215
- const cardObserver = new IntersectionObserver((entries, observer) => {
1216
- entries.forEach(entry => {
1217
- if (entry.isIntersecting) {
1218
- entry.target.classList.add('visible');
1219
- observer.unobserve(entry.target);
1220
- }
1221
- });
1222
- }, {
1223
- root: null,
1224
- rootMargin: '0px',
1225
- threshold: 0.1
1226
- });
1227
-
1228
- menuCards.forEach(card => cardObserver.observe(card));
1229
-
1230
- const toggleLinks = document.querySelectorAll('.toggle-details');
1231
- toggleLinks.forEach(link => {
1232
- link.addEventListener('click', function () {
1233
- const itemName = this.getAttribute('data-item-name').replace(/ /g, '-');
1234
- const detailsDiv = document.getElementById(`details-${itemName}`);
1235
- detailsDiv.classList.toggle('show');
1236
- this.innerText = detailsDiv.classList.contains('show') ? 'Hide Details' : 'Show Details';
1237
- });
1238
- });
1239
-
1240
- const categoryButtons = document.querySelectorAll('.category-button');
1241
- const categoryForm = document.getElementById('categoryForm');
1242
- const selectedCategoryInput = document.getElementById('selectedCategoryInput');
1243
-
1244
- if (!selectedCategoryInput.value) {
1245
- selectedCategoryInput.value = "All";
1246
- document.querySelector('.category-button[data-category="All"]').classList.add('selected');
1247
- }
1248
-
1249
- categoryButtons.forEach(button => {
1250
- button.addEventListener('click', function () {
1251
- categoryButtons.forEach(btn => btn.classList.remove('selected'));
1252
- this.classList.add('selected');
1253
- selectedCategoryInput.value = this.getAttribute('data-category');
1254
- categoryForm.submit();
1255
- });
1256
- });
1257
-
1258
- const searchBar = document.getElementById('searchBar');
1259
- const suggestionsContainer = document.getElementById('autocompleteSuggestions');
1260
-
1261
- searchBar.addEventListener('input', function () {
1262
- const input = this.value.trim().toLowerCase();
1263
- suggestionsContainer.innerHTML = '';
1264
- suggestionsContainer.style.display = 'none';
1265
-
1266
- if (input) {
1267
- const filteredItems = menuItems.filter(item =>
1268
- item.toLowerCase().includes(input)
1269
- );
1270
- if (filteredItems.length > 0) {
1271
- filteredItems.forEach(item => {
1272
- const suggestionDiv = document.createElement('div');
1273
- suggestionDiv.classList.add('suggestion-item');
1274
- suggestionDiv.innerText = item;
1275
- suggestionDiv.addEventListener('click', function () {
1276
- searchBar.value = item;
1277
- suggestionsContainer.style.display = 'none';
1278
- filterMenu();
1279
- });
1280
- suggestionsContainer.appendChild(suggestionDiv);
1281
- });
1282
- suggestionsContainer.style.display = 'block';
1283
- }
1284
- }
1285
- filterMenu();
1286
- });
1287
-
1288
- document.addEventListener('click', function (event) {
1289
- if (!searchBar.contains(event.target) && !suggestionsContainer.contains(event.target)) {
1290
- suggestionsContainer.style.display = 'none';
1291
- }
1292
- });
1293
-
1294
- const descriptionTextarea = document.getElementById('custom-dish-description');
1295
- const descriptionSuggestions = document.getElementById('descriptionSuggestions');
1296
-
1297
- if (descriptionTextarea && descriptionSuggestions) {
1298
- let usedIngredients = new Set();
1299
-
1300
- function updateUsedIngredients() {
1301
- const inputText = descriptionTextarea.value.trim();
1302
- usedIngredients.clear();
1303
- if (inputText) {
1304
- const words = inputText.split(/,\s*/).map(word => word.trim());
1305
- words.forEach(word => {
1306
- if (word && ingredientsList.includes(word)) {
1307
- usedIngredients.add(word);
1308
- }
1309
- });
1310
- }
1311
- }
1312
-
1313
- descriptionTextarea.addEventListener('input', function () {
1314
- const inputText = this.value.trim();
1315
- const words = inputText.split(/,\s*/);
1316
- const lastWord = words[words.length - 1].trim().toLowerCase();
1317
- descriptionSuggestions.innerHTML = '';
1318
- descriptionSuggestions.style.display = 'none';
1319
-
1320
- updateUsedIngredients();
1321
-
1322
- if (lastWord) {
1323
- const filteredIngredients = ingredientsList.filter(ingredient =>
1324
- ingredient.toLowerCase().includes(lastWord) && !usedIngredients.has(ingredient)
1325
- );
1326
- if (filteredIngredients.length > 0) {
1327
- filteredIngredients.forEach(ingredient => {
1328
- const suggestionDiv = document.createElement('div');
1329
- suggestionDiv.classList.add('suggestion-item');
1330
- suggestionDiv.innerText = ingredient;
1331
- suggestionDiv.addEventListener('click', function () {
1332
- const currentValue = descriptionTextarea.value;
1333
- const lastCommaIndex = currentValue.lastIndexOf(',');
1334
- const baseText = lastCommaIndex !== -1 ? currentValue.substring(0, lastCommaIndex + 1) : '';
1335
- descriptionTextarea.value = baseText + (baseText ? ' ' : '') + ingredient + ', ';
1336
- descriptionSuggestions.style.display = 'none';
1337
- descriptionTextarea.focus();
1338
- updateUsedIngredients();
1339
- });
1340
- descriptionSuggestions.appendChild(suggestionDiv);
1341
- });
1342
- descriptionSuggestions.style.display = 'block';
1343
- }
1344
- }
1345
- });
1346
-
1347
- document.addEventListener('click', function (event) {
1348
- if (!descriptionTextarea.contains(event.target) && !descriptionSuggestions.contains(event.target)) {
1349
- descriptionSuggestions.style.display = 'none';
1350
- }
1351
- });
1352
- }
1353
-
1354
- fetch('/cart/get')
1355
- .then(response => {
1356
- if (!response.ok) {
1357
- throw new Error(`HTTP error! Status: ${response.status}`);
1358
- }
1359
- return response.json();
1360
- })
1361
- .then(data => {
1362
- if (data.success) {
1363
- updateCartUI(data.cart);
1364
- } else {
1365
- console.error('Failed to fetch cart:', data.error);
1366
- const cart = getCartLocalStorage();
1367
- updateCartUI(cart);
1368
- }
1369
- })
1370
- .catch(err => {
1371
- console.error('Error fetching cart:', err);
1372
- const cart = getCartLocalStorage();
1373
- updateCartUI(cart);
1374
- });
1375
-
1376
- const preloadedImages = document.querySelectorAll('link[rel="preload"]');
1377
- preloadedImages.forEach(link => {
1378
- const img = new Image();
1379
- img.src = link.href;
1380
- });
1381
-
1382
- const decreaseBtn = document.getElementById('decreaseQuantity');
1383
- const increaseBtn = document.getElementById('increaseQuantity');
1384
- const quantityInput = document.getElementById('quantityInput');
1385
-
1386
- decreaseBtn.addEventListener('click', function () {
1387
- let currentQuantity = parseInt(quantityInput.value);
1388
- if (currentQuantity > 1) {
1389
- currentQuantity--;
1390
- quantityInput.value = currentQuantity;
1391
- }
1392
- });
1393
-
1394
- increaseBtn.addEventListener('click', function () {
1395
- let currentQuantity = parseInt(quantityInput.value);
1396
- currentQuantity++;
1397
- quantityInput.value = currentQuantity;
1398
- });
1399
- });
1400
-
1401
- function debounce(func, wait) {
1402
- let timeout;
1403
- return function (...args) {
1404
- clearTimeout(timeout);
1405
- timeout = setTimeout(() => func.apply(this, args), wait);
1406
- };
1407
- }
1408
-
1409
- function filterMenu() {
1410
- const input = document.getElementById('searchBar').value.trim().toLowerCase();
1411
- const sections = document.querySelectorAll('h3');
1412
- const items = document.querySelectorAll('.menu-card');
1413
- let matchedSections = new Set();
1414
-
1415
- items.forEach(item => {
1416
- const itemName = item.querySelector('.card-title').innerText.toLowerCase();
1417
- const itemSection = item.closest('.row').previousElementSibling.innerText.toLowerCase();
1418
-
1419
- if (itemName.includes(input) || (itemSection && itemSection.includes(input))) {
1420
- item.style.display = 'block';
1421
- item.classList.add('visible');
1422
- matchedSections.add(item.closest('.row'));
1423
- } else {
1424
- item.style.display = 'none';
1425
- }
1426
- });
1427
-
1428
- sections.forEach(section => {
1429
- const sectionRow = section.nextElementSibling;
1430
- if (matchedSections.has(sectionRow)) {
1431
- section.style.display = 'block';
1432
- sectionRow.style.display = 'flex';
1433
- } else {
1434
- section.style.display = 'none';
1435
- sectionRow.style.display = 'none';
1436
- }
1437
- });
1438
-
1439
- if (!input) {
1440
- sections.forEach(section => {
1441
- section.style.display = 'block';
1442
- section.nextElementSibling.style.display = 'flex';
1443
- });
1444
- items.forEach(item => {
1445
- item.style.display = 'block';
1446
- item.classList.add('visible');
1447
- });
1448
- }
1449
- }
1450
-
1451
- function showItemDetails(name, price, image, description, section, selectedCategory) {
1452
- document.getElementById('modal-name').innerText = name;
1453
- document.getElementById('modal-price').innerText = `$${price}`;
1454
- const modalImg = document.getElementById('modal-img');
1455
- if (section.toLowerCase() === 'soft drinks') {
1456
- modalImg.style.display = 'none';
1457
- } else {
1458
- modalImg.style.display = 'block';
1459
- modalImg.src = image || '/static/placeholder.jpg';
1460
- }
1461
- document.getElementById('modal-description').innerText = description || 'No description available.';
1462
- const nutritionalInfoEl = document.getElementById('modal-nutritional-info');
1463
- const itemDetails = menuItemDetails[name];
1464
- if (itemDetails && itemDetails.nutritionalInfo) {
1465
- const { calories, protein, carbs, fats, fiber, sugar } = itemDetails.nutritionalInfo;
1466
- nutritionalInfoEl.innerText = `[Energy: ${calories} kcal, Protein: ${protein}g, Carbohydrates: ${carbs}g, Fiber: ${fiber}g, Fat: ${fats}g, Sugar: ${sugar}g]`;
1467
- } else {
1468
- nutritionalInfoEl.innerText = '[No nutritional info available.]';
1469
- }
1470
-
1471
- document.getElementById('addons-list').innerHTML = 'Loading customization options...';
1472
- document.getElementById('modal-instructions').value = '';
1473
- const modalSectionEl = document.getElementById('modal-section');
1474
- modalSectionEl.setAttribute('data-section', section);
1475
- modalSectionEl.setAttribute('data-category', selectedCategory);
1476
- document.getElementById('quantityInput').value = 1;
1477
-
1478
- const prepStyleOptions = document.getElementById('prep-style-options');
1479
- const typeOptions = document.getElementById('type-options');
1480
- const spiceLevelOptions = document.getElementById('spice-level-options');
1481
- const firstRow = document.getElementById('first-row');
1482
- const firstRowTitle = document.getElementById('first-row-title');
1483
- const addonsTitle = document.getElementById('addons-title');
1484
-
1485
- prepStyleOptions.innerHTML = '';
1486
- typeOptions.innerHTML = '';
1487
- spiceLevelOptions.innerHTML = '';
1488
-
1489
- if (section.toLowerCase() === 'starters') {
1490
- firstRow.style.display = 'block';
1491
- firstRowTitle.style.display = 'block';
1492
- addonsTitle.style.display = 'none';
1493
- } else {
1494
- firstRow.style.display = 'none';
1495
- firstRowTitle.style.display = 'none';
1496
- addonsTitle.style.display = 'block';
1497
- }
1498
-
1499
- const addonsList = document.getElementById('addons-list');
1500
- addonsList.innerHTML = '';
1501
- const dummySections = [
1502
- { name: "Beverages", options: ["Sprite ($3)", "Thums Up ($3)", "Virgin Mojito ($3)", "Lemonade ($3)", "Blue Lagoon Mocktail ($3)"] },
1503
- { name: "Sauces", options: ["Mint Chutney", "Tomato Sauce"] },
1504
- { name: "Extra Toppings", options: ["Cheese ($2)", "Olives ($1)", "Jalapenos ($1)", "Mushrooms ($2)", "Peppers ($1)"] },
1505
- { name: "Sides", options: ["Fries ($3)", "Salad ($2)", "Garlic Bread ($3)", "Onion Rings ($2)", "Coleslaw ($2)"] },
1506
- { name: "Desserts", options: ["Ice Cream ($3)", "Brownie ($3)", "Cheesecake ($4)", "Gulab Jamun ($3)", "Rasmalai ($4)"] }
1507
- ];
1508
-
1509
- dummySections.forEach(addon => {
1510
- const sectionDiv = document.createElement('div');
1511
- sectionDiv.classList.add('addon-section');
1512
- sectionDiv.setAttribute('data-addon-name', addon.name);
1513
-
1514
- const title = document.createElement('h6');
1515
- title.innerText = addon.name;
1516
- sectionDiv.appendChild(title);
1517
-
1518
- const optionsContainer = document.createElement('div');
1519
- addon.options.forEach((option, index) => {
1520
- const optionId = `addon-${addon.name.replace(/\s+/g, '')}-${index}`;
1521
- const listItem = document.createElement('div');
1522
- listItem.classList.add('form-check');
1523
-
1524
- listItem.innerHTML = `
1525
- <input type="checkbox" class="form-check-input addon-option" id="${optionId}" value="${option}"
1526
- data-name="${option}" data-group="${addon.name}" data-price="0">
1527
- <label class="form-check-label" for="${optionId}">
1528
- ${option}
1529
- </label>
1530
- `;
1531
- optionsContainer.appendChild(listItem);
1532
- });
1533
- sectionDiv.appendChild(optionsContainer);
1534
- addonsList.appendChild(sectionDiv);
1535
- });
1536
-
1537
- fetch(`/api/addons?item_name=${encodeURIComponent(name)}&item_section=${encodeURIComponent(section)}`)
1538
- .then(response => response.json())
1539
- .then(data => {
1540
- const addonsList = document.getElementById('addons-list');
1541
- addonsList.innerHTML = '';
1542
-
1543
- if (!data.success || !data.addons || data.addons.length === 0) {
1544
- addonsList.innerHTML = '<p>No customization options available.</p>';
1545
- addonsTitle.style.display = 'none';
1546
- return;
1547
- }
1548
-
1549
- if (section.toLowerCase() === 'starters') {
1550
- data.addons.forEach(addon => {
1551
- if (addon.name.toLowerCase() === "choose preparation style") {
1552
- addon.options.forEach(option => {
1553
- const optionId = `addon-prep-style-${option}`;
1554
- const optionHTML = `
1555
- <div class="form-check">
1556
- <input type="checkbox" class="form-check-input" id="${optionId}" value="${option}" data-name="${option}" data-group="Choose Preparation Style">
1557
- <label class="form-check-label" for="${optionId}">
1558
- ${option}
1559
- </label>
1560
- </div>
1561
- `;
1562
- prepStyleOptions.innerHTML += optionHTML;
1563
- });
1564
- }
1565
- if (addon.name.toLowerCase() === "type") {
1566
- addon.options.forEach(option => {
1567
- const optionId = `addon-type-${option}`;
1568
- const optionHTML = `
1569
- <div class="form-check">
1570
- <input type="checkbox" class="form-check-input" id="${optionId}" value="${option}" data-name="${option}" data-group="Type">
1571
- <label class="form-check-label" for="${optionId}">
1572
- ${option}
1573
- </label>
1574
- </div>
1575
- `;
1576
- typeOptions.innerHTML += optionHTML;
1577
- });
1578
- }
1579
- if (addon.name.toLowerCase() === "spice level") {
1580
- addon.options.forEach(option => {
1581
- const optionId = `addon-spice-level-${option}`;
1582
- const optionHTML = `
1583
- <div class="form-check">
1584
- <input type="checkbox" class="form-check-input spice-level-option" id="${optionId}" value="${option}" data-name="${option}" data-group="Spice Level">
1585
- <label class="form-check-label" for="${optionId}">
1586
- ${option}
1587
- </label>
1588
- </div>
1589
- `;
1590
- spiceLevelOptions.innerHTML += optionHTML;
1591
- });
1592
- }
1593
- });
1594
- }
1595
-
1596
- data.addons.forEach(addon => {
1597
- if (section.toLowerCase() === 'starters' &&
1598
- (addon.name.toLowerCase() === "type" ||
1599
- addon.name.toLowerCase() === "spice level" ||
1600
- addon.name.toLowerCase() === "choose preparation style")) {
1601
- return;
1602
- }
1603
-
1604
- const sectionDiv = document.createElement('div');
1605
- sectionDiv.classList.add('addon-section');
1606
- sectionDiv.setAttribute('data-addon-name', addon.name);
1607
-
1608
- const title = document.createElement('h6');
1609
- title.innerText = addon.name;
1610
- sectionDiv.appendChild(title);
1611
-
1612
- const optionsContainer = document.createElement('div');
1613
- addon.options.forEach((option, index) => {
1614
- const optionId = `addon-${addon.name.replace(/\s+/g, '')}-${index}`;
1615
- const listItem = document.createElement('div');
1616
- listItem.classList.add('form-check');
1617
-
1618
- listItem.innerHTML = `
1619
- <input type="checkbox" class="form-check-input ${addon.name.toLowerCase() === 'spice level' ? 'spice-level-option' : 'addon-option'}" id="${optionId}" value="${option}"
1620
- data-name="${option}" data-group="${addon.name}" data-price="${addon.extra_charge ? addon.extra_charge_amount : 0}">
1621
- <label class="form-check-label" for="${optionId}">
1622
- ${option} ${addon.extra_charge ? `($${addon.extra_charge_amount})` : ''}
1623
- </label>
1624
- `;
1625
- optionsContainer.appendChild(listItem);
1626
- });
1627
- sectionDiv.appendChild(optionsContainer);
1628
- addonsList.appendChild(sectionDiv);
1629
- });
1630
-
1631
- const startersOrder = [
1632
- "Select Dip/Sauce",
1633
- "Extra Add-ons",
1634
- "Make it a Combo"
1635
- ];
1636
-
1637
- const desiredOrder = [
1638
- "Spice Level",
1639
- "Choose Preparation Style",
1640
- "Select Dip/Sauce",
1641
- "Extra Add-ons",
1642
- "Make it a Combo",
1643
- "Type"
1644
- ];
1645
-
1646
- const orderToUse = section.toLowerCase() === 'starters' ? startersOrder : desiredOrder;
1647
-
1648
- const sections = Array.from(addonsList.children);
1649
- addonsList.innerHTML = '';
1650
-
1651
- orderToUse.forEach(sectionName => {
1652
- const section = sections.find(s => s.getAttribute('data-addon-name') === sectionName);
1653
- if (section) {
1654
- addonsList.appendChild(section);
1655
- }
1656
- });
1657
-
1658
- sections.forEach(section => {
1659
- if (!orderToUse.includes(section.getAttribute('data-addon-name'))) {
1660
- addonsList.appendChild(section);
1661
- }
1662
- });
1663
- })
1664
- .catch(err => {
1665
- console.error('Error fetching add-ons:', err);
1666
- document.getElementById('addons-list').innerHTML = '<p>Error loading customization options.</p>';
1667
- addonsTitle.style.display = 'none';
1668
- });
1669
- }
1670
-
1671
- document.addEventListener('click', function(event) {
1672
- if (event.target.classList.contains('spice-level-option') || event.target.classList.contains('addon-option')) {
1673
- handleAddonClick(event.target);
1674
- }
1675
- });
1676
-
1677
- function handleAddonClick(checkbox) {
1678
- const groupName = checkbox.getAttribute('data-group');
1679
- const isMultiSelectGroup = ["Extra Toppings", "Choose Raita/Sides", "Select Dip/Sauce", "Extra Add-ons", "Make it a Combo", "Beverages", "Sauces"].includes(groupName);
1680
-
1681
- if (groupName.toLowerCase() === "spice level") {
1682
- const allSpiceLevelCheckboxes = document.querySelectorAll('.spice-level-option');
1683
- allSpiceLevelCheckboxes.forEach(otherCheckbox => {
1684
- if (otherCheckbox !== checkbox) {
1685
- otherCheckbox.checked = false;
1686
- }
1687
- });
1688
- }
1689
- else if (!isMultiSelectGroup) {
1690
- const checkboxes = document.querySelectorAll(`.addon-option[data-group="${groupName}"]`);
1691
- checkboxes.forEach(otherCheckbox => {
1692
- if (otherCheckbox !== checkbox) {
1693
- otherCheckbox.checked = false;
1694
- }
1695
- });
1696
- }
1697
- }
1698
-
1699
- function addToCartFromModal() {
1700
- const itemName = document.getElementById('modal-name').innerText;
1701
- let itemPrice = parseFloat(document.getElementById('modal-price').innerText.replace('$', ''));
1702
- if (isNaN(itemPrice)) {
1703
- alert('Invalid price for the item. Please check the item details.');
1704
- return;
1705
- }
1706
- const itemImage = document.getElementById('modal-img').src;
1707
- const modalSectionEl = document.getElementById('modal-section');
1708
- const section = modalSectionEl.getAttribute('data-section');
1709
- const selectedCategory = modalSectionEl.getAttribute('data-category');
1710
- if (!itemName || !itemPrice || !section || !itemImage) {
1711
- console.error('Missing data for cart item:', { itemName, itemPrice, section, itemImage});
1712
- return;
1713
- }
1714
-
1715
- let selectedAddOns = [];
1716
- const addonsListOptions = document.querySelectorAll('#addons-list .addon-option');
1717
- addonsListOptions.forEach(option => {
1718
- if (option.checked) {
1719
- selectedAddOns.push({
1720
- name: option.getAttribute('data-name') || 'Default Name',
1721
- price: parseFloat(option.getAttribute('data-price') || 0)
1722
- });
1723
- }
1724
- });
1725
-
1726
- if (section.toLowerCase() === 'starters') {
1727
- const prepStyleOptions = Array.from(
1728
- document.querySelectorAll('#prep-style-options input[type="checkbox"]:checked')
1729
- ).map(option => ({
1730
- name: option.getAttribute('data-name') || 'Default Prep Style',
1731
- price: 0
1732
- }));
1733
- const typeOptions = Array.from(
1734
- document.querySelectorAll('#type-options input[type="checkbox"]:checked')
1735
- ).map(option => ({
1736
- name: option.getAttribute('data-name') || 'Default Type',
1737
- price: 0
1738
- }));
1739
- const spiceLevelOption = document.querySelector('#spice-level-options input[type="checkbox"].spice-level-option:checked');
1740
- const spiceLevelOptions = spiceLevelOption ? [{
1741
- name: spiceLevelOption.getAttribute('data-name') || 'Default Spice Level',
1742
- price: 0
1743
- }] : [];
1744
- selectedAddOns = [...selectedAddOns, ...prepStyleOptions, ...typeOptions, ...spiceLevelOptions];
1745
- }
1746
- const quantity = parseInt(document.getElementById('quantityInput').value) || 1;
1747
- const instructions = document.getElementById('modal-instructions').value;
1748
- const cartPayload = {
1749
- itemName: itemName,
1750
- itemPrice: itemPrice,
1751
- itemImage: itemImage,
1752
- section: section,
1753
- category: selectedCategory,
1754
- addons: selectedAddOns,
1755
- instructions: instructions,
1756
- quantity: quantity
1757
- };
1758
- fetch('/cart/add', {
1759
- method: 'POST',
1760
- headers: {
1761
- 'Content-Type': 'application/json',
1762
- },
1763
- body: JSON.stringify(cartPayload)
1764
- })
1765
- .then(response => {
1766
- if (!response.ok) {
1767
- throw new Error(`HTTP error! Status: ${response.status}`);
1768
- }
1769
- return response.json();
1770
- })
1771
- .then(data => {
1772
- if (data.success) {
1773
- alert('Item added to cart successfully!');
1774
- updateCartUI(data.cart);
1775
- const modal = document.getElementById('itemModal');
1776
- const modalInstance = bootstrap.Modal.getInstance(modal);
1777
- modalInstance.hide();
1778
- } else {
1779
- console.error('Failed to add item to cart:', data.error);
1780
- alert(data.error || 'Failed to add item to cart.');
1781
- }
1782
- })
1783
- .catch(err => {
1784
- console.error('Error adding item to cart:', err);
1785
- alert('An error occurred while adding the item to the cart: ' + err.message);
1786
- const cart = addToCartLocalStorage(cartPayload);
1787
- updateCartUI(cart);
1788
- const modal = document.getElementById('itemModal');
1789
- const modalInstance = bootstrap.Modal.getInstance(modal);
1790
- modalInstance.hide();
1791
- });
1792
- }
1793
- function handleSoftDrinkAdd(button) {
1794
- const buttonContainer = button.closest('.button-container');
1795
- const quantitySelector = buttonContainer.querySelector('.quantity-selector');
1796
- const addButton = buttonContainer.querySelector('.add-to-cart-btn');
1797
- const quantityDisplay = quantitySelector.querySelector('.quantity-display');
1798
- const quantityToAddSelect = quantitySelector.querySelector('.quantity-to-add');
1799
- const quantityToAdd = parseInt(quantityToAddSelect.value);
1800
- const itemName = buttonContainer.getAttribute('data-item-name');
1801
- const itemPrice = parseFloat(buttonContainer.getAttribute('data-item-price'));
1802
- const itemImage = buttonContainer.getAttribute('data-item-image');
1803
- const section = buttonContainer.getAttribute('data-item-section');
1804
- const selectedCategory = buttonContainer.getAttribute('data-item-category');
1805
- addButton.style.display = 'none';
1806
- quantitySelector.style.display = 'flex';
1807
- let currentQuantity = parseInt(quantityDisplay.innerText) || 0;
1808
- currentQuantity += quantityToAdd;
1809
- quantityDisplay.innerText = currentQuantity;
1810
- const cartPayload = {
1811
- itemName: itemName,
1812
- itemPrice: itemPrice,
1813
- itemImage: itemImage,
1814
- section: section,
1815
- category: selectedCategory,
1816
- addons: [],
1817
- instructions: '',
1818
- quantity: quantityToAdd
1819
- };
1820
- button.disabled = true;
1821
- fetch('/cart/add', {
1822
- method: 'POST',
1823
- headers: {
1824
- 'Content-Type': 'application/json',
1825
- },
1826
- body: JSON.stringify(cartPayload)
1827
- })
1828
- .then(response => {
1829
- if (!response.ok) {
1830
- throw new Error(`HTTP error! Status: ${response.status}`);
1831
- }
1832
- return response.json();
1833
- })
1834
- .then(data => {
1835
- if (data.success) {
1836
- alert(`Added ${quantityToAdd} item(s) to cart successfully!`);
1837
- updateCartUI(data.cart);
1838
- } else {
1839
- console.error('Failed to add item to cart:', data.error);
1840
- alert(data.error || 'Failed to add item to cart.');
1841
- currentQuantity -= quantityToAdd;
1842
- quantityDisplay.innerText = currentQuantity;
1843
- }
1844
- })
1845
- .catch(err => {
1846
- console.error('Error adding item to cart:', err);
1847
- alert('An error occurred while adding the item to the cart: ' + err.message);
1848
- const cart = addToCartLocalStorage(cartPayload);
1849
- updateCartUI(cart);
1850
- })
1851
- .finally(() => {
1852
- button.disabled = false;
1853
- });
1854
- }
1855
- function increaseQuantity(button) {
1856
- const buttonContainer = button.closest('.button-container');
1857
- const quantityDisplay = buttonContainer.querySelector('.quantity-display');
1858
- const quantityToAddSelect = buttonContainer.querySelector('.quantity-to-add');
1859
- const quantityToAdd = parseInt(quantityToAddSelect.value);
1860
- const itemName = buttonContainer.getAttribute('data-item-name');
1861
- const itemPrice = parseFloat(buttonContainer.getAttribute('data-item-price'));
1862
- const itemImage = buttonContainer.getAttribute('data-item-image');
1863
- const section = buttonContainer.getAttribute('data-item-section');
1864
- const selectedCategory = buttonContainer.getAttribute('data-item-category');
1865
- let currentQuantity = parseInt(quantityDisplay.innerText) || 0;
1866
- currentQuantity += quantityToAdd;
1867
- quantityDisplay.innerText = currentQuantity;
1868
- const cartPayload = {
1869
- itemName: itemName,
1870
- itemPrice: itemPrice,
1871
- itemImage: itemImage,
1872
- section: section,
1873
- category: selectedCategory,
1874
- addons: [],
1875
- instructions: '',
1876
- quantity: quantityToAdd
1877
- };
1878
- fetch('/cart/add', {
1879
- method: 'POST',
1880
- headers: {
1881
- 'Content-Type': 'application/json',
1882
- },
1883
- body: JSON.stringify(cartPayload)
1884
- })
1885
- .then(response => {
1886
- if (!response.ok) {
1887
- throw new Error(`HTTP error! Status: ${response.status}`);
1888
- }
1889
- return response.json();
1890
- })
1891
- .then(data => {
1892
- if (data.success) {
1893
- alert(`Added ${quantityToAdd} item(s) to cart successfully!`);
1894
- updateCartUI(data.cart);
1895
- } else {
1896
- console.error('Failed to add item to cart:', data.error);
1897
- alert(data.error || 'Failed to add item to cart.');
1898
- currentQuantity -= quantityToAdd;
1899
- quantityDisplay.innerText = currentQuantity;
1900
- }
1901
- })
1902
- .catch(err => {
1903
- console.error('Error adding item to cart:', err);
1904
- alert('An error occurred while adding the item to the cart: ' + err.message);
1905
- const cart = addToCartLocalStorage(cartPayload);
1906
- updateCartUI(cart);
1907
- });
1908
- }
1909
- function decreaseQuantity(button) {
1910
- const buttonContainer = button.closest('.button-container');
1911
- const quantityDisplay = buttonContainer.querySelector('.quantity-display');
1912
- const quantityToRemoveSelect = buttonContainer.querySelector('.quantity-to-remove');
1913
- const quantityToRemove = parseInt(quantityToRemoveSelect.value);
1914
- const addButton = buttonContainer.querySelector('.add-to-cart-btn');
1915
- const quantitySelector = buttonContainer.querySelector('.quantity-selector');
1916
- let currentQuantity = parseInt(quantityDisplay.innerText);
1917
- if (currentQuantity <= quantityToRemove) {
1918
- const itemName = buttonContainer.getAttribute('data-item-name');
1919
- fetch(`/cart/remove?item_name=${encodeURIComponent(itemName)}&quantity=${currentQuantity}&instructions=&addons=[]`, {
1920
- method: 'POST',
1921
- headers: {
1922
- 'Content-Type': 'application/json',
1923
- }
1924
- })
1925
- .then(response => {
1926
- if (!response.ok) {
1927
- throw new Error(`HTTP error! Status: ${response.status}`);
1928
- }
1929
- return response.json();
1930
- })
1931
- .then(data => {
1932
- if (data.success) {
1933
- updateCartUI(data.cart);
1934
- addButton.style.display = 'block';
1935
- quantitySelector.style.display = 'none';
1936
- quantityDisplay.innerText = 0;
1937
- } else {
1938
- console.error('Failed to remove item from cart:', data.error);
1939
- alert(data.error || 'Failed to remove item from cart.');
1940
- }
1941
- })
1942
- .catch(err => {
1943
- console.error('Error removing item from cart:', err);
1944
- alert('An error occurred while removing the item from the cart: ' + err.message);
1945
- const cart = removeFromCartLocalStorage(itemName, currentQuantity, '', []);
1946
- updateCartUI(cart);
1947
- addButton.style.display = 'block';
1948
- quantitySelector.style.display = 'none';
1949
- quantityDisplay.innerText = 0;
1950
- });
1951
- } else {
1952
- currentQuantity -= quantityToRemove;
1953
- quantityDisplay.innerText = currentQuantity;
1954
- const itemName = buttonContainer.getAttribute('data-item-name');
1955
- fetch(`/cart/remove?item_name=${encodeURIComponent(itemName)}&quantity=${quantityToRemove}&instructions=&addons=[]`, {
1956
- method: 'POST',
1957
- headers: {
1958
- 'Content-Type': 'application/json',
1959
- }
1960
- })
1961
- .then(response => {
1962
- if (!response.ok) {
1963
- throw new Error(`HTTP error! Status: ${response.status}`);
1964
- }
1965
- return response.json();
1966
- })
1967
- .then(data => {
1968
- if (data.success) {
1969
- alert(`Removed ${quantityToRemove} item(s) from cart successfully!`);
1970
- updateCartUI(data.cart);
1971
- } else {
1972
- console.error('Failed to remove item from cart:', data.error);
1973
- alert(data.error || 'Failed to remove item from cart.');
1974
- currentQuantity += quantityToRemove;
1975
- quantityDisplay.innerText = currentQuantity;
1976
- }
1977
- })
1978
- .catch(err => {
1979
- console.error('Error removing item from cart:', err);
1980
- alert('An error occurred while removing the item from the cart: ' + err.message);
1981
- const cart = removeFromCartLocalStorage(itemName, quantityToRemove, '', []);
1982
- updateCartUI(cart);
1983
- });
1984
- }
1985
- }
1986
- function updateCartUI(cart) {
1987
- if (!Array.isArray(cart)) {
1988
- console.error('Invalid cart data:', cart);
1989
- return;
1990
- }
1991
-
1992
- // Calculate total quantity of items in cart
1993
- let totalQuantity = 0;
1994
- cart.forEach(item => {
1995
- totalQuantity += item.quantity;
1996
- });
1997
-
1998
- // Update cart item count badge
1999
- const cartItemCount = document.getElementById('cart-item-count');
2000
- if (cartItemCount) {
2001
- cartItemCount.innerText = totalQuantity;
2002
- if (totalQuantity > 0) {
2003
- cartItemCount.classList.add('active');
2004
- } else {
2005
- cartItemCount.classList.remove('active');
2006
- }
2007
- }
2008
- // Update quantity displays for soft drinks
2009
- const buttonContainers = document.querySelectorAll('.button-container');
2010
- buttonContainers.forEach(container => {
2011
- const itemName = container.getAttribute('data-item-name');
2012
- const quantityDisplay = container.querySelector('.quantity-display');
2013
- const addButton = container.querySelector('.add-to-cart-btn');
2014
- const quantitySelector = container.querySelector('.quantity-selector');
2015
- const cartItem = cart.find(item =>
2016
- item.itemName === itemName &&
2017
- item.instructions === '' &&
2018
- JSON.stringify(item.addons) === JSON.stringify([])
2019
- );
2020
- if (cartItem && cartItem.quantity > 0) {
2021
- quantityDisplay.innerText = cartItem.quantity;
2022
- addButton.style.display = 'none';
2023
- quantitySelector.style.display = 'flex';
2024
- } else {
2025
- quantityDisplay.innerText = 0;
2026
- addButton.style.display = 'block';
2027
- quantitySelector.style.display = 'none';
2028
- }
2029
- });
2030
- }
2031
- </script>
2032
- </body>
2033
- </html>
 
1
+ from flask import Blueprint, render_template, request, session, jsonify, redirect, url_for
2
+ from salesforce import get_salesforce_connection
3
+ import os
4
+
5
+ menu_blueprint = Blueprint('menu', __name__)
6
+
7
+ # Initialize Salesforce connection
8
+ sf = get_salesforce_connection()
9
+
10
+ # Constants for video handling
11
+ STATIC_DIR = os.path.join(os.path.dirname(__file__), 'static')
12
+ PLACEHOLDER_VIDEO = 'placeholder.mp4'
13
+ PLACEHOLDER_PATH = os.path.join(STATIC_DIR, PLACEHOLDER_VIDEO)
14
+ SECTION_ORDER = ["Best Sellers", "Starters", "Biryanis", "Curries", "Breads", "Customized dish", "Apetizer", "Desserts", "Soft Drinks"]
15
+
16
+ # Create placeholder video at startup if it doesn't exist
17
+ if not os.path.exists(PLACEHOLDER_PATH):
18
+ open(PLACEHOLDER_PATH, 'wb').close()
19
+ print(f"Created placeholder video at {PLACEHOLDER_PATH}")
20
+
21
+ def get_valid_video_path(item_name, video_url=None):
22
+ """
23
+ Get valid video path for item with placeholder fallback
24
+ Priority: 1. Video1__c from Salesforce 2. placeholder.mp4
25
+ """
26
+ # First try: Video1__c from Salesforce if provided
27
+ if video_url:
28
+ # If it's a complete URL (http/https)
29
+ if video_url.startswith(('http://', 'https://')):
30
+ return video_url
31
+ # If it's a relative path (/videos/xxx.mp4)
32
+ elif video_url.startswith('/'):
33
+ return video_url
34
+ # If it's a Salesforce File ID (starts with '069')
35
+ elif video_url.startswith('069'):
36
+ return f"https://yourdomain.my.salesforce.com/sfc/servlet.shepherd/version/download/{video_url}"
37
+
38
+ # Final fallback: placeholder.mp4
39
+ if not os.path.exists(PLACEHOLDER_PATH):
40
+ open(PLACEHOLDER_PATH, 'wb').close()
41
+ print(f"Created missing placeholder video at {PLACEHOLDER_PATH}")
42
+
43
+ return f"/static/{PLACEHOLDER_VIDEO}"
44
+
45
+ @menu_blueprint.route("/menu", methods=["GET", "POST"])
46
+ def menu():
47
+ selected_category = request.args.get("category", "All")
48
+ user_email = session.get('user_email')
49
+
50
+ if not user_email:
51
+ user_email = request.args.get("email")
52
+ user_name = request.args.get("name")
53
+
54
+ if user_email:
55
+ session['user_email'] = user_email
56
+ session['user_name'] = user_name
57
+ else:
58
+ return redirect(url_for("login"))
59
+ else:
60
+ user_name = session.get('user_name')
61
+
62
+ first_letter = user_name[0].upper() if user_name else "A"
63
+
64
+ try:
65
+ # Fetch user referral and reward points
66
+ user_query = f"SELECT Referral__c, Reward_Points__c FROM Customer_Login__c WHERE Email__c = '{user_email}'"
67
+ user_result = sf.query(user_query)
68
+
69
+ if not user_result['records']:
70
+ return redirect(url_for('login'))
71
+
72
+ referral_code = user_result['records'][0].get('Referral__c', 'N/A')
73
+ reward_points = user_result['records'][0].get('Reward_Points__c', 0)
74
+
75
+ # Get cart item count
76
+ cart_query = f"SELECT COUNT() FROM Cart_Item__c WHERE Customer_Email__c = '{user_email}'"
77
+ cart_count_result = sf.query(cart_query)
78
+ cart_item_count = cart_count_result['totalSize']
79
+
80
+ # Query to fetch Menu_Item__c records including Video1__c
81
+ menu_query = """
82
+ SELECT Name, Price__c, Description__c, Image1__c, Image2__c,
83
+ Veg_NonVeg__c, Section__c, Total_Ordered__c, Video1__c
84
+ FROM Menu_Item__c
85
+ """
86
+ result = sf.query(menu_query)
87
+ food_items = result['records'] if 'records' in result else []
88
+
89
+ # Process items and add video paths
90
+ for item in food_items:
91
+ if 'Total_Ordered__c' not in item or item['Total_Ordered__c'] is None:
92
+ item['Total_Ordered__c'] = 0
93
+ item['Video1__c'] = get_valid_video_path(item['Name'], item.get('Video1__c'))
94
+
95
+ # Query to fetch Custom_Dish__c records
96
+ custom_dish_query = """
97
+ SELECT Name, Price__c, Description__c, Image1__c, Image2__c,
98
+ Veg_NonVeg__c, Section__c, Total_Ordered__c
99
+ FROM Custom_Dish__c
100
+ WHERE CreatedDate >= LAST_N_DAYS:7
101
+ """
102
+ custom_dish_result = sf.query(custom_dish_query)
103
+ custom_dishes = custom_dish_result.get('records', [])
104
+
105
+ # Process custom dishes and add video paths
106
+ for item in custom_dishes:
107
+ if 'Total_Ordered__c' not in item or item['Total_Ordered__c'] is None:
108
+ item['Total_Ordered__c'] = 0
109
+ item['Video1__c'] = get_valid_video_path(item['Name'])
110
+
111
+ # Merge both Menu_Item__c and Custom_Dish__c records
112
+ all_items = food_items + custom_dishes
113
+ ordered_menu = {section: [] for section in SECTION_ORDER}
114
+
115
+ # Process best sellers
116
+ best_sellers = sorted(all_items, key=lambda x: x.get("Total_Ordered__c", 0), reverse=True)
117
 
118
+ if selected_category == "Veg":
119
+ best_sellers = [item for item in best_sellers if item.get("Veg_NonVeg__c") in ["Veg", "both"]]
120
+ elif selected_category == "Non veg":
121
+ best_sellers = [item for item in best_sellers if item.get("Veg_NonVeg__c") in ["Non veg", "both"]]
122
+
123
+ best_sellers = best_sellers[:4]
124
+ if best_sellers:
125
+ ordered_menu["Best Sellers"] = best_sellers
126
+
127
+ # Organize other sections
128
+ added_item_names = set()
129
+ for item in all_items:
130
+ section = item.get("Section__c", "Others")
131
+ if section not in ordered_menu:
132
+ ordered_menu[section] = []
133
+
134
+ if item['Name'] in added_item_names:
135
+ continue
136
+
137
+ if selected_category == "Veg" and item.get("Veg_NonVeg__c") not in ["Veg", "both"]:
138
+ continue
139
+ if selected_category == "Non veg" and item.get("Veg_NonVeg__c") not in ["Non veg", "both"]:
140
+ continue
141
+
142
+ ordered_menu[section].append(item)
143
+ added_item_names.add(item['Name'])
144
+
145
+ ordered_menu = {section: items for section, items in ordered_menu.items() if items}
146
+ categories = ["All", "Veg", "Non veg"]
147
+
148
+ except Exception as e:
149
+ print(f"Error fetching menu data: {str(e)}")
150
+ # Fallback data with video support
151
+ ordered_menu = {section: [] for section in SECTION_ORDER}
152
+ best_sellers = ["Chicken Biryani", "Paneer Butter Masala", "Veg Manchurian", "Prawn Fry"]
153
+ ordered_menu["Best Sellers"] = [{
154
+ "Name": name,
155
+ "Price__c": "12.99",
156
+ "Description__c": f"Popular {name}",
157
+ "Image1__c": "/static/placeholder.jpg",
158
+ "Video1__c": get_valid_video_path(name),
159
+ "Total_Ordered__c": 100,
160
+ "Veg_NonVeg__c": "Veg" if "Paneer" in name or "Veg" in name else "Non veg"
161
+ } for name in best_sellers]
162
 
163
+ categories = ["All", "Veg", "Non veg"]
164
+ referral_code = 'N/A'
165
+ reward_points = 0
166
+ cart_item_count = 0
167
+
168
+ return render_template(
169
+ "menu.html",
170
+ ordered_menu=ordered_menu,
171
+ categories=categories,
172
+ selected_category=selected_category,
173
+ referral_code=referral_code,
174
+ reward_points=reward_points,
175
+ user_name=user_name,
176
+ first_letter=first_letter,
177
+ cart_item_count=cart_item_count
178
+ )
179
+
180
+ # [Rest of your existing routes...]