templates/base.html.twig line 1

Open in your IDE?
  1. <!DOCTYPE html>
  2. <html lang="fr">
  3.     <head>
  4.         <meta charset="UTF-8">
  5.         <meta http-equiv="X-UA-Compatible" content="IE=edge">
  6.         <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
  7.         <meta name="description" content="">
  8.         <meta name="author" content="ventes en ligne, rapeto service, madagascar">
  9.         <title>{% block title %} Dashboard Templates !{% endblock %}</title>
  10.         <link rel="icon" href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 128 128%22><text y=%221.2em%22 font-size=%2296%22>⚫️</text></svg>">
  11.         <link rel="stylesheet" href="https://cdn.datatables.net/1.13.8/css/dataTables.bootstrap5.min.css">
  12.         {# Run `composer require symfony/webpack-encore-bundle` to start using Symfony UX #}
  13.         {% block stylesheets %}
  14.             {{ encore_entry_link_tags('app') }}
  15.             <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.1.0/css/all.min.css">
  16.             <link href="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/css/select2.min.css" rel="stylesheet" />
  17.                 <style>
  18.                 
  19.                     /* Dans votre fichier CSS */
  20.                     .logo-overlay {
  21.                         position: absolute;
  22.                         top: 0;
  23.                         right: 0;
  24.                         width: 50%; /* Ajustez la largeur du logo selon vos besoins */
  25.                         height: auto;
  26.                         pointer-events: none;
  27.                         z-index: 10;
  28.                     }
  29.                     /* S'assurer que le wrapper a position: relative */
  30.                     .image-wrapper {
  31.                         position: relative !important;
  32.                         display: inline-block; /* ou selon votre layout */
  33.                     }
  34.                     /**/
  35.                     .mega-dropdown {
  36.                         position: fixed;          /* 👈 important */
  37.                         left: 50%;
  38.                         transform: translateX(-26%);
  39.                         /*width: 1200px;*/
  40.                         max-width: 95vw;
  41.                         max-height: 60vh; /* 60% of viewport height */
  42.                         overflow-y: auto; /* scroll if content exceeds max-height */
  43.                         background: #fff;
  44.                         border-radius: 15px;
  45.                         box-shadow: 0 15px 40px rgba(0,0,0,0.08);
  46.                         z-index: 9999;
  47.                         padding-top: 40px;     
  48.                     }
  49.                     /**/
  50.                     .category-header {
  51.                         padding: 10px 14px;
  52.                         background: #f8f9fa;
  53.                         border-radius: 10px;
  54.                     }
  55.                     .category-title {
  56.                         font-size: 0.95rem;
  57.                         font-weight: 600;
  58.                         letter-spacing: 0.3px;
  59.                         text-transform: capitalize; /* backup capitalization */
  60.                     }
  61.                     .view-all {
  62.                         color: #0d6efd;
  63.                         transition: 0.2s ease;
  64.                     }
  65.                     .view-all:hover {
  66.                         opacity: 0.7;
  67.                     }
  68.                     .divider {
  69.                         height: 1px;
  70.                         background: linear-gradient(to right, #e9ecef, transparent);
  71.                     }
  72.                     /* Add smooth appearance */
  73.                     .mega-dropdown.show {
  74.                         animation: fadeDown 0.25s ease;
  75.                     }
  76.                     @keyframes fadeDown {
  77.                         from {
  78.                             opacity: 0;
  79.                             transform: translateY(-10px);
  80.                         }
  81.                         to {
  82.                             opacity: 1;
  83.                             transform: translateY(0);
  84.                         }
  85.                     }
  86.                     .product-item {
  87.                         cursor: pointer;
  88.                         transition: transform 0.2s ease;
  89.                     }
  90.                     .product-item:hover {
  91.                         transform: translateY(-3px);
  92.                     }
  93.                     .product-img-wrapper {
  94.                         background: #f8f9fa;
  95.                         padding: 8px;
  96.                         border-radius: 8px;
  97.                     }
  98.                     .product-item img {
  99.                         width: 100%;
  100.                         height: 100px;
  101.                         object-fit: contain;
  102.                     }     
  103.                 </style>
  104.         {% endblock %}
  105.         {% block javascripts %}
  106.             {{ encore_entry_script_tags('app') }}
  107.         {% endblock %}
  108.     </head>
  109.     <body id="page-top">
  110.         {% block body %}{% endblock %}
  111.         <script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
  112.         <!-- Select2 JS -->
  113.         <script src="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.min.js"></script>
  114.         <script src="https://cdn.datatables.net/1.13.8/js/jquery.dataTables.min.js"></script>
  115.         <script src="https://cdn.datatables.net/1.13.8/js/dataTables.bootstrap5.min.js"></script>
  116.         <script>
  117.             $(document).ready(function() {
  118.                 $('.select2').select2({
  119.                     placeholder: "Choisir un ou plusieurs services",
  120.                     allowClear: true,
  121.                 });
  122.             });
  123.         </script>
  124.         {# <script>
  125.             $(document).ready(function () {
  126.                 $('.datatable').DataTable({
  127.                     pageLength: 25,
  128.                     ordering: true,
  129.                     searching: true,
  130.                     info: true,
  131.                     language: {
  132.                         url: "//cdn.datatables.net/plug-ins/1.13.8/i18n/fr-FR.json"
  133.                     },
  134.                 });
  135.             });
  136.         </script> #}
  137.         <script>
  138.             $(document).ready(function () {
  139.                 $('.datatable').DataTable({
  140.                     pageLength: 25,
  141.                     ordering: true,
  142.                     searching: true,
  143.                     info: true,
  144.                     language: {
  145.                         decimal: ',',
  146.                         thousands: ' ',
  147.                         emptyTable: 'Aucune donnée disponible',
  148.                         info: 'Affichage de _START_ à _END_ sur _TOTAL_ entrées',
  149.                         infoEmpty: 'Affichage de 0 à 0 sur 0 entrée',
  150.                         infoFiltered: '(filtré depuis _MAX_ entrées au total)',
  151.                         lengthMenu: 'Afficher _MENU_ entrées',
  152.                         loadingRecords: 'Chargement...',
  153.                         processing: 'Traitement...',
  154.                         search: 'Rechercher :',
  155.                         zeroRecords: 'Aucun enregistrement correspondant trouvé',
  156.                         paginate: {
  157.                             first: 'Premier',
  158.                             last: 'Dernier',
  159.                             next: 'Suivant',
  160.                             previous: 'Précédent'
  161.                         }
  162.                     }
  163.                 });
  164.             });
  165.         </script>
  166.         <script>
  167.             document.querySelectorAll('.product-link').forEach(link => {
  168.                 link.addEventListener('click', function () {
  169.                     const url = this.dataset.clickUrl;
  170.                     if (navigator.sendBeacon) {
  171.                         navigator.sendBeacon(url);
  172.                     } else {
  173.                         fetch(url, { method: 'POST', keepalive: true });
  174.                     }
  175.                 });
  176.             });
  177.            const logoSrc = '/img/logo-rapeto-img.png';
  178.             function addLogoToProductImages() {
  179.                 document.querySelectorAll(".image-wrapper").forEach(wrapper => {
  180.                     // Maka ny sary produit
  181.                     const productImg = wrapper.querySelector("img");
  182.                     // Raha tsy misy sary, dia tsy mamorona logo
  183.                     if (!productImg) return;
  184.                     // Raha efa misy logo, tsy mamorona indray
  185.                     if (wrapper.querySelector(".logo-overlay")) return;
  186.                     // Mamorona logo
  187.                     const logo = document.createElement("img");
  188.                     logo.src = logoSrc;
  189.                     logo.alt = "Logo";
  190.                     logo.className = "logo-overlay";
  191.                     wrapper.appendChild(logo);
  192.                 });
  193.             }
  194.             // Alefa rehefa loaded ny DOM
  195.             document.addEventListener("DOMContentLoaded", addLogoToProductImages);
  196.         </script>
  197.         <script>
  198.             const productClickBaseUrl = "{{ path('app_product_click_search', {'id': 'PRODUCT_ID'}) }}";
  199.             const productShowBaseUrl  = "{{ path('app_product_show', {'id': 'PRODUCT_ID'}) }}";
  200.         </script>
  201.         <script>
  202.             document.addEventListener("DOMContentLoaded", function () {
  203.                 const input = document.getElementById("search-input");
  204.                 const suggestionsBox = document.getElementById("search-suggestions");
  205.                 const suggestionsBoxResults = document.getElementById("search-suggestions-results");
  206.                 input.addEventListener("focus", function () {
  207.                     if (this.value.length >= 2) return;
  208.                     fetch("{{ path('ajax_random_products') }}")
  209.                         .then(response => response.json())
  210.                         .then(data => {
  211.                             const categoryContainer = document.getElementById("categoryResults");
  212.                             categoryContainer.innerHTML = "";
  213.                             if (!data || data.length === 0) {
  214.                                 categoryContainer.innerHTML = `
  215.                                     <div class="text-center text-muted py-4">
  216.                                         Aucun produit disponible.
  217.                                     </div>
  218.                                 `;
  219.                             }
  220.                             (data || []).forEach(group => {
  221.                                 let block = document.createElement("div");
  222.                                 block.classList.add("mb-4");
  223.                                 // block.innerHTML = `
  224.                                 //     <div class="d-flex justify-content-between align-items-center mb-2">
  225.                                 //         <div class="fw-bold">${group.categoryName.charAt(0).toUpperCase() + group.categoryName.slice(1)}</div>
  226.                                 //         <a href="#" class="small text-primary"></a>
  227.                                 //     </div>
  228.                                 //     <div class="row g-3" id="cat-${group.categoryId}"></div>
  229.                                 // `;
  230.                                 block.innerHTML = `
  231.                                     <div class="category-header d-flex justify-content-between align-items-center mb-3">
  232.                                         <h6 class="category-title mb-0">
  233.                                             ${group.categoryName 
  234.                                                 ? group.categoryName.charAt(0).toUpperCase() + group.categoryName.slice(1) 
  235.                                                 : ''}
  236.                                         </h6>
  237.                                         <a href="#" class="small text-decoration-none fw-semibold view-all"></a>
  238.                                     </div>
  239.                                     <div class="divider mb-3"></div>
  240.                                     <div class="row g-3" id="cat-${group.categoryId}"></div>
  241.                                 `;
  242.                                 categoryContainer.appendChild(block);
  243.                                 let row = block.querySelector(`#cat-${group.categoryId}`);
  244.                                 group.products.slice(0, 4).forEach(product => {
  245.                                     let col = document.createElement("div");
  246.                                     // col.classList.add("col-3");
  247.                                     col.classList.add("col-6", "col-md-4", "col-lg-3");
  248.                                     col.innerHTML = `
  249.                                         <div class="product-item">
  250.                                             <img src="/uploads/products/${product.image}">
  251.                                             <div class="small mt-2">${product.title}</div>
  252.                                         </div>
  253.                                     `;
  254.                                     col.querySelector(".product-item").addEventListener("click", function (e) {
  255.                                         e.preventDefault();
  256.                                         let clickUrl = productClickBaseUrl.replace('PRODUCT_ID', product.id);
  257.                                         let showUrl  = productShowBaseUrl.replace('PRODUCT_ID', product.id);
  258.                                         fetch(clickUrl + "?fromSearch=1", {
  259.                                             method: "POST",
  260.                                             headers: {
  261.                                                 "X-Requested-With": "XMLHttpRequest"
  262.                                             }
  263.                                         })
  264.                                         .then(() => {
  265.                                             window.location.href = showUrl;
  266.                                         });
  267.                                     });
  268.                                     row.appendChild(col);
  269.                                 });
  270.                             });
  271.                             // suggestionsBox.style.display = "block";
  272.                             suggestionsBox.style.display = "block";
  273.                             suggestionsBox.classList.add("show");
  274.                         });
  275.                 });
  276.                 input.addEventListener("keyup", function () {
  277.                     let query = this.value;
  278.                     if (query.length < 2) {
  279.                         suggestionsBox.style.display = "none";
  280.                         return;
  281.                     }
  282.                     fetch("{{ path('ajax_search') }}?q=" + query)
  283.                         .then(response => response.json())
  284.                         .then(data => {
  285.                             suggestionsBoxResults.innerHTML = "";
  286.                             if (data.length === 0) {
  287.                                 suggestionsBoxResults.innerHTML = `
  288.                                     <div class="list-group-item text-muted">
  289.                                         Aucun produit trouvé.
  290.                                     </div>
  291.                                 `;
  292.                                 suggestionsBoxResults.style.display = "block";
  293.                                 return;
  294.                             }
  295.                            data.forEach(product => {
  296.                                 let item = document.createElement("a");
  297.                                 item.href = "#"; // prevent default link behavior
  298.                                 item.classList.add("list-group-item", "list-group-item-action");
  299.                                 item.innerHTML = `
  300.                                     <div class="d-flex align-items-center">
  301.                                         <img src="{{ asset('uploads/products/') }}${product.image}"
  302.                                             width="40"
  303.                                             height="40"
  304.                                             class="me-2 rounded">
  305.                                         <div>
  306.                                             <div>${product.title}</div>
  307.                                         </div>
  308.                                     </div>
  309.                                 `;
  310.                                 item.addEventListener("click", function (e) {
  311.                                     e.preventDefault();
  312.                                     let clickUrl = productClickBaseUrl.replace('PRODUCT_ID', product.id);
  313.                                     let showUrl  = productShowBaseUrl.replace('PRODUCT_ID', product.id);
  314.                                     fetch(clickUrl + "?fromSearch=1", {
  315.                                         method: "POST",
  316.                                         headers: {
  317.                                             "X-Requested-With": "XMLHttpRequest"
  318.                                         }
  319.                                     })
  320.                                     .then(() => {
  321.                                         window.location.href = showUrl;
  322.                                     });
  323.                                 });
  324.                                 suggestionsBoxResults.appendChild(item);
  325.                             });
  326.                             suggestionsBoxResults.style.display = "block";
  327.                         });
  328.                 });
  329.                 // Hide when clicking outside
  330.                 // document.addEventListener("click", function (e) {
  331.                 //     if (!input.contains(e.target)) {
  332.                 //         suggestionsBox.style.display = "none";
  333.                 //     }
  334.                 // });
  335.                 document.addEventListener("click", function (e) {
  336.                     if (!input.contains(e.target) && !suggestionsBox.contains(e.target)) {
  337.                         suggestionsBox.style.display = "none";
  338.                     }
  339.                 });
  340.                 // document.addEventListener("click", function (e) {
  341.                 //     if (!input.contains(e.target) && !suggestionsBoxResults.contains(e.target)) {
  342.                 //         suggestionsBoxResults.style.display = "none";
  343.                 //     }
  344.                 // });
  345.             });
  346.         </script>
  347.     </body>
  348. </html>