lokesh341 commited on
Commit
40f227c
·
verified ·
1 Parent(s): 514c2c8

Update templates/menu.html

Browse files
Files changed (1) hide show
  1. templates/menu.html +66 -153
templates/menu.html CHANGED
@@ -7,6 +7,8 @@
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
  <style>
11
  body {
12
  font-family: Arial, sans-serif;
@@ -27,6 +29,11 @@
27
  margin: auto;
28
  display: flex;
29
  flex-direction: column;
 
 
 
 
 
30
  }
31
  .menu-image {
32
  height: 200px;
@@ -332,24 +339,6 @@
332
  height: 40px;
333
  width: 40px;
334
  }
335
- .loading {
336
- display: none;
337
- text-align: center;
338
- margin: 20px 0;
339
- }
340
- .loading-spinner {
341
- border: 4px solid #f3f3f3;
342
- border-top: 4px solid #0FAA39;
343
- border-radius: 50%;
344
- width: 30px;
345
- height: 30px;
346
- animation: spin 1s linear infinite;
347
- display: inline-block;
348
- }
349
- @keyframes spin {
350
- 0% { transform: rotate(0deg); }
351
- 100% { transform: rotate(360deg); }
352
- }
353
  @media (max-width: 576px) {
354
  .modal-dialog {
355
  max-width: 98%;
@@ -379,7 +368,7 @@
379
  </div>
380
  </div>
381
  <div class="search-bar-container">
382
- <input type="text" id="searchBar" class="form-control" placeholder="Search items or sections..." onkeyup="filterMenu()">
383
  <i class="bi bi-search search-icon"></i>
384
  </div>
385
  </div>
@@ -400,7 +389,7 @@
400
  </div>
401
  </form>
402
 
403
- <div class="container mt-4" id="menu-container">
404
  {% if selected_category == "Customized Dish" %}
405
  <div id="custom-dish-form" class="mt-4">
406
  <h3>Create Your Custom Dish</h3>
@@ -417,56 +406,41 @@
417
  </form>
418
  </div>
419
  {% else %}
420
- <div id="menu-items">
421
- {% for section, items in ordered_menu.items() %}
422
- <h3>{{ section }}</h3>
423
- <div class="row" data-section="{{ section }}">
424
- {% for item in items %}
425
- <div class="col-md-6 mb-4">
426
- <div class="card menu-card">
427
- <img src="{{ item.Image1__c }}"
428
- srcset="{{ item.Image1__c }}?w=350 350w, {{ item.Image1__c }}?w=200 200w"
429
- sizes="(max-width: 576px) 200px, 350px"
430
- class="card-img-top menu-image"
431
- alt="{{ item.Name }}"
432
- onerror="this.src='/static/placeholder.jpg';"
433
- loading="lazy">
434
- <div class="addbutton">
435
- <div class="card-body d-flex align-items-center justify-content-between">
436
  <div>
437
- <h5 class="card-title">{{ item.Name }}</h5>
438
- <p class="card-text">${{ item.Price__c }}</p>
 
 
439
  </div>
440
- <div class="d-flex flex-column align-item-center justify-content-center">
441
- <div>
442
- <button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#itemModal"
443
- onclick="showItemDetails('{{ item.Name }}', '{{ item.Price__c }}', '{{ item.Image2__c }}', '{{ item.Description__c }}', '{{ item.Section__c }}','{{ selected_category }}')">
444
- ADD
445
- </button>
446
- </div>
447
- <div class="w-100 text-center">
448
- {% if item.Section__c != 'Apetizer' and item.Section__c != 'Customized dish' and item.Section__c !='Soft Drinks' %}
449
- <h5 class="customisable-text">Customisable</h5>
450
- {% endif %}
451
- </div>
452
  </div>
453
  </div>
454
  </div>
455
  </div>
456
  </div>
457
- {% endfor %}
458
- </div>
459
- {% endfor %}
460
- </div>
461
- <div id="loading" class="loading">
462
- <div class="loading-spinner"></div>
463
- <p>Loading more items...</p>
464
- </div>
465
- {% if has_more %}
466
- <div id="load-more" class="text-center my-4">
467
- <button class="btn btn-primary" onclick="loadMoreItems()">Load More</button>
468
  </div>
469
- {% endif %}
470
  {% endif %}
471
  </div>
472
 
@@ -531,91 +505,35 @@
531
  <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"></script>
532
 
533
  <script>
534
- let currentPage = {{ page | default(1) }};
535
- const perPage = {{ per_page | default(10) }};
536
- const selectedCategory = '{{ selected_category }}';
537
-
538
- function loadMoreItems() {
539
- const loadingEl = document.getElementById('loading');
540
- const loadMoreEl = document.getElementById('load-more');
541
- loadingEl.style.display = 'block';
542
- loadMoreEl.style.display = 'none';
543
-
544
- currentPage++;
545
- fetch(`/menu?category=${encodeURIComponent(selectedCategory)}&page=${currentPage}&per_page=${perPage}&ajax=1`)
546
- .then(response => response.json())
547
- .then(data => {
548
- if (data.ordered_menu) {
549
- appendMenuItems(data.ordered_menu);
550
- if (!data.has_more) {
551
- loadMoreEl.style.display = 'none';
552
- } else {
553
- loadMoreEl.style.display = 'block';
554
- }
555
  }
556
- loadingEl.style.display = 'none';
557
- })
558
- .catch(err => {
559
- console.error('Error loading more items:', err);
560
- alert('Failed to load more items.');
561
- loadingEl.style.display = 'none';
562
- loadMoreEl.style.display = 'block';
563
  });
564
- }
565
-
566
- function appendMenuItems(orderedMenu) {
567
- const menuItemsContainer = document.getElementById('menu-items');
568
- for (const [section, items] of Object.entries(orderedMenu)) {
569
- let sectionEl = document.querySelector(`.row[data-section="${section}"]`);
570
- if (!sectionEl) {
571
- const sectionTitle = document.createElement('h3');
572
- sectionTitle.innerText = section;
573
- menuItemsContainer.appendChild(sectionTitle);
574
 
575
- sectionEl = document.createElement('div');
576
- sectionEl.classList.add('row');
577
- sectionEl.setAttribute('data-section', section);
578
- menuItemsContainer.appendChild(sectionEl);
579
- }
580
 
581
- items.forEach(item => {
582
- const itemHTML = `
583
- <div class="col-md-6 mb-4">
584
- <div class="card menu-card">
585
- <img src="${item.Image1__c}"
586
- srcset="${item.Image1__c}?w=350 350w, ${item.Image1__c}?w=200 200w"
587
- sizes="(max-width: 576px) 200px, 350px"
588
- class="card-img-top menu-image"
589
- alt="${item.Name}"
590
- onerror="this.src='/static/placeholder.jpg';"
591
- loading="lazy">
592
- <div class="addbutton">
593
- <div class="card-body d-flex align-items-center justify-content-between">
594
- <div>
595
- <h5 class="card-title">${item.Name}</h5>
596
- <p class="card-text">$${item.Price__c}</p>
597
- </div>
598
- <div class="d-flex flex-column align-item-center justify-content-center">
599
- <div>
600
- <button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#itemModal"
601
- onclick="showItemDetails('${item.Name}', '${item.Price__c}', '${item.Image2__c}', '${item.Description__c}', '${item.Section__c}', '${selectedCategory}')">
602
- ADD
603
- </button>
604
- </div>
605
- <div class="w-100 text-center">
606
- ${item.Section__c !== 'Apetizer' && item.Section__c !== 'Customized dish' && item.Section__c !== 'Soft Drinks'
607
- ? '<h5 class="customisable-text">Customisable</h5>'
608
- : ''}
609
- </div>
610
- </div>
611
- </div>
612
- </div>
613
- </div>
614
- </div>
615
- `;
616
- sectionEl.insertAdjacentHTML('beforeend', itemHTML);
617
- });
618
- }
619
  }
620
 
621
  function showItemDetails(name, price, image, description, section, selectedCategory) {
@@ -817,7 +735,7 @@
817
 
818
  function filterMenu() {
819
  let input = document.getElementById('searchBar').value.toLowerCase();
820
- let sections = document.querySelectorAll('#menu-items h3');
821
  let items = document.querySelectorAll('.menu-card');
822
  let matchedSections = new Set();
823
 
@@ -827,6 +745,7 @@
827
 
828
  if (itemName.includes(input) || (itemSection && itemSection.includes(input))) {
829
  item.style.display = 'block';
 
830
  matchedSections.add(item.closest('.row'));
831
  } else {
832
  item.style.display = 'none';
@@ -843,17 +762,11 @@
843
  sectionRow.style.display = 'none';
844
  }
845
  });
846
-
847
- const visibleItems = document.querySelectorAll('.menu-card:not([style*="display: none"])');
848
- const totalItems = {{ total_items | default(0) }};
849
- const loadMoreEl = document.getElementById('load-more');
850
- if (visibleItems.length < totalItems && input === '') {
851
- loadMoreEl.style.display = 'block';
852
- } else {
853
- loadMoreEl.style.display = 'none';
854
- }
855
  }
856
 
 
 
 
857
  function addToCartFromModal() {
858
  const itemName = document.getElementById('modal-name').innerText;
859
  let itemPrice = parseFloat(document.getElementById('modal-price').innerText.replace('$', ''));
 
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 Placeholder Image -->
11
+ <link rel="preload" href="/static/placeholder.jpg" as="image">
12
  <style>
13
  body {
14
  font-family: Arial, sans-serif;
 
29
  margin: auto;
30
  display: flex;
31
  flex-direction: column;
32
+ opacity: 0; /* Initially hidden for lazy loading */
33
+ transition: opacity 0.3s ease-in-out;
34
+ }
35
+ .menu-card.visible {
36
+ opacity: 1; /* Visible when in viewport */
37
  }
38
  .menu-image {
39
  height: 200px;
 
339
  height: 40px;
340
  width: 40px;
341
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
342
  @media (max-width: 576px) {
343
  .modal-dialog {
344
  max-width: 98%;
 
368
  </div>
369
  </div>
370
  <div class="search-bar-container">
371
+ <input type="text" id="searchBar" class="form-control" placeholder="Search items or sections...">
372
  <i class="bi bi-search search-icon"></i>
373
  </div>
374
  </div>
 
389
  </div>
390
  </form>
391
 
392
+ <div class="container mt-4">
393
  {% if selected_category == "Customized Dish" %}
394
  <div id="custom-dish-form" class="mt-4">
395
  <h3>Create Your Custom Dish</h3>
 
406
  </form>
407
  </div>
408
  {% else %}
409
+ {% for section, items in ordered_menu.items() %}
410
+ <h3>{{ section }}</h3>
411
+ <div class="row">
412
+ {% for item in items %}
413
+ <div class="col-md-6 mb-4">
414
+ <div class="card menu-card">
415
+ <img src="{{ item.Image1__c }}" class="card-img-top menu-image" alt="{{ item.Name }}"
416
+ width="350" height="200" loading="lazy"
417
+ onerror="this.src='/static/placeholder.jpg';">
418
+ <div class="addbutton">
419
+ <div class="card-body d-flex align-items-center justify-content-between">
420
+ <div>
421
+ <h5 class="card-title">{{ item.Name }}</h5>
422
+ <p class="card-text">${{ item.Price__c }}</p>
423
+ </div>
424
+ <div class="d-flex flex-column align-item-center justify-content-center">
425
  <div>
426
+ <button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#itemModal"
427
+ onclick="showItemDetails('{{ item.Name }}', '{{ item.Price__c }}', '{{ item.Image2__c }}', '{{ item.Description__c }}', '{{ item.Section__c }}','{{ selected_category }}')">
428
+ ADD
429
+ </button>
430
  </div>
431
+ <div class="w-100 text-center">
432
+ {% if item.Section__c != 'Apetizer' and item.Section__c != 'Customized dish' and item.Section__c !='Soft Drinks' %}
433
+ <h5 class="customisable-text">Customisable</h5>
434
+ {% endif %}
 
 
 
 
 
 
 
 
435
  </div>
436
  </div>
437
  </div>
438
  </div>
439
  </div>
440
+ </div>
441
+ {% endfor %}
 
 
 
 
 
 
 
 
 
442
  </div>
443
+ {% endfor %}
444
  {% endif %}
445
  </div>
446
 
 
505
  <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"></script>
506
 
507
  <script>
508
+ // Lazy Load Menu Items with Intersection Observer
509
+ document.addEventListener('DOMContentLoaded', function () {
510
+ const menuCards = document.querySelectorAll('.menu-card');
511
+
512
+ const observer = new IntersectionObserver((entries, observer) => {
513
+ entries.forEach(entry => {
514
+ if (entry.isIntersecting) {
515
+ entry.target.classList.add('visible');
516
+ observer.unobserve(entry.target); // Stop observing once visible
 
 
 
 
 
 
 
 
 
 
 
 
517
  }
 
 
 
 
 
 
 
518
  });
519
+ }, {
520
+ root: null,
521
+ rootMargin: '0px',
522
+ threshold: 0.1 // Trigger when 10% of the card is visible
523
+ });
 
 
 
 
 
524
 
525
+ menuCards.forEach(card => {
526
+ observer.observe(card);
527
+ });
528
+ });
 
529
 
530
+ // Debounce Function for Search
531
+ function debounce(func, wait) {
532
+ let timeout;
533
+ return function (...args) {
534
+ clearTimeout(timeout);
535
+ timeout = setTimeout(() => func.apply(this, args), wait);
536
+ };
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
537
  }
538
 
539
  function showItemDetails(name, price, image, description, section, selectedCategory) {
 
735
 
736
  function filterMenu() {
737
  let input = document.getElementById('searchBar').value.toLowerCase();
738
+ let sections = document.querySelectorAll('h3');
739
  let items = document.querySelectorAll('.menu-card');
740
  let matchedSections = new Set();
741
 
 
745
 
746
  if (itemName.includes(input) || (itemSection && itemSection.includes(input))) {
747
  item.style.display = 'block';
748
+ item.classList.add('visible'); // Ensure visible items are marked as such
749
  matchedSections.add(item.closest('.row'));
750
  } else {
751
  item.style.display = 'none';
 
762
  sectionRow.style.display = 'none';
763
  }
764
  });
 
 
 
 
 
 
 
 
 
765
  }
766
 
767
+ // Add Debounce to Search
768
+ document.getElementById('searchBar').addEventListener('keyup', debounce(filterMenu, 300));
769
+
770
  function addToCartFromModal() {
771
  const itemName = document.getElementById('modal-name').innerText;
772
  let itemPrice = parseFloat(document.getElementById('modal-price').innerText.replace('$', ''));