templates/base.html.twig line 241

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.                     .stock-unavailable-modal .modal-dialog {
  104.                         max-width: 430px;
  105.                         padding: 1rem;
  106.                     }
  107.                     .stock-unavailable-modal .modal-content {
  108.                         overflow: hidden;
  109.                         border: 1px solid rgba(226, 232, 240, .9);
  110.                         border-radius: 24px;
  111.                         background: #fff;
  112.                         box-shadow: 0 24px 70px rgba(15, 23, 42, .22);
  113.                     }
  114.                     .stock-unavailable-modal .modal-header {
  115.                         position: relative;
  116.                         justify-content: center;
  117.                         padding: 1.6rem 3.5rem .25rem;
  118.                         border: 0;
  119.                     }
  120.                     .stock-unavailable-modal .modal-title {
  121.                         color: #172b4d;
  122.                         font-size: 1.25rem;
  123.                         font-weight: 750;
  124.                         letter-spacing: -.025em;
  125.                         text-align: center;
  126.                     }
  127.                     .stock-unavailable-modal .btn-close {
  128.                         position: absolute;
  129.                         top: 1rem;
  130.                         right: 1rem;
  131.                         padding: .65rem;
  132.                         border-radius: 50%;
  133.                         background-color: #f1f5f9;
  134.                         opacity: .8;
  135.                     }
  136.                     .stock-unavailable-modal .modal-body {
  137.                         padding: .8rem 2rem 1.35rem;
  138.                         color: #64748b;
  139.                         text-align: center;
  140.                     }
  141.                     .stock-unavailable-modal .modal-body::before {
  142.                         display: flex;
  143.                         width: 62px;
  144.                         height: 62px;
  145.                         align-items: center;
  146.                         justify-content: center;
  147.                         margin: 0 auto 1rem;
  148.                         border: 7px solid #fff7ed;
  149.                         border-radius: 50%;
  150.                         background: #ffedd5;
  151.                         color: #c2410c;
  152.                         content: "!";
  153.                         font-size: 1.7rem;
  154.                         font-weight: 800;
  155.                     }
  156.                     .stock-unavailable-modal .modal-body strong {
  157.                         display: block;
  158.                         margin-bottom: .35rem;
  159.                         color: #334155;
  160.                         font-size: 1rem;
  161.                         font-weight: 700;
  162.                     }
  163.                     .stock-unavailable-modal .modal-body p {
  164.                         margin: 0;
  165.                         font-size: .94rem;
  166.                         line-height: 1.6;
  167.                     }
  168.                     .stock-unavailable-modal .modal-footer {
  169.                         justify-content: center;
  170.                         padding: 0 1.5rem 1.5rem;
  171.                         border: 0;
  172.                     }
  173.                     .stock-unavailable-modal .modal-footer .btn {
  174.                         min-width: 145px;
  175.                         padding: .7rem 1.35rem;
  176.                         border: 0;
  177.                         border-radius: 999px;
  178.                         background: #155e52;
  179.                         box-shadow: 0 8px 18px rgba(21, 94, 82, .2);
  180.                         font-weight: 650;
  181.                         transition: transform .18s ease, background-color .18s ease, box-shadow .18s ease;
  182.                     }
  183.                     .stock-unavailable-modal .modal-footer .btn:hover {
  184.                         transform: translateY(-1px);
  185.                         background: #104a41;
  186.                         box-shadow: 0 10px 22px rgba(21, 94, 82, .28);
  187.                     }
  188.                     @media (max-width: 480px) {
  189.                         .stock-unavailable-modal .modal-dialog {
  190.                             margin: .75rem auto;
  191.                         }
  192.                         .stock-unavailable-modal .modal-content {
  193.                             border-radius: 20px;
  194.                         }
  195.                     }
  196.                 </style>
  197.         {% endblock %}
  198.         {% block javascripts %}
  199.             {{ encore_entry_script_tags('app') }}
  200.         {% endblock %}
  201.     </head>
  202.     <body id="page-top">
  203.         {% block body %}{% endblock %}
  204.         <script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
  205.         <!-- Select2 JS -->
  206.         <script src="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.min.js"></script>
  207.         <script src="https://cdn.datatables.net/1.13.8/js/jquery.dataTables.min.js"></script>
  208.         <script src="https://cdn.datatables.net/1.13.8/js/dataTables.bootstrap5.min.js"></script>
  209.         <script>
  210.             $(document).ready(function() {
  211.                 $('.select2').select2({
  212.                     placeholder: "Choisir un ou plusieurs services",
  213.                     allowClear: true,
  214.                 });
  215.             });
  216.         </script>
  217.         {# <script>
  218.             $(document).ready(function () {
  219.                 $('.datatable').DataTable({
  220.                     pageLength: 25,
  221.                     ordering: true,
  222.                     searching: true,
  223.                     info: true,
  224.                     language: {
  225.                         url: "//cdn.datatables.net/plug-ins/1.13.8/i18n/fr-FR.json"
  226.                     },
  227.                 });
  228.             });
  229.         </script> #}
  230.         <script>
  231.             $(document).ready(function () {
  232.                 $('.datatable').DataTable({
  233.                     pageLength: 25,
  234.                     ordering: true,
  235.                     searching: true,
  236.                     info: true,
  237.                     language: {
  238.                         decimal: ',',
  239.                         thousands: ' ',
  240.                         emptyTable: 'Aucune donnée disponible',
  241.                         info: 'Affichage de _START_ à _END_ sur _TOTAL_ entrées',
  242.                         infoEmpty: 'Affichage de 0 à 0 sur 0 entrée',
  243.                         infoFiltered: '(filtré depuis _MAX_ entrées au total)',
  244.                         lengthMenu: 'Afficher _MENU_ entrées',
  245.                         loadingRecords: 'Chargement...',
  246.                         processing: 'Traitement...',
  247.                         search: 'Rechercher :',
  248.                         zeroRecords: 'Aucun enregistrement correspondant trouvé',
  249.                         paginate: {
  250.                             first: 'Premier',
  251.                             last: 'Dernier',
  252.                             next: 'Suivant',
  253.                             previous: 'Précédent'
  254.                         }
  255.                     }
  256.                 });
  257.             });
  258.         </script>
  259.         <script>
  260.             document.querySelectorAll('.product-link').forEach(link => {
  261.                 link.addEventListener('click', function () {
  262.                     const url = this.dataset.clickUrl;
  263.                     if (navigator.sendBeacon) {
  264.                         navigator.sendBeacon(url);
  265.                     } else {
  266.                         fetch(url, { method: 'POST', keepalive: true });
  267.                     }
  268.                 });
  269.             });
  270.            const logoSrc = '/img/logo-rapeto-img.png';
  271.             function addLogoToProductImages() {
  272.                 document.querySelectorAll(".image-wrapper").forEach(wrapper => {
  273.                     // Maka ny sary produit
  274.                     const productImg = wrapper.querySelector("img");
  275.                     // Raha tsy misy sary, dia tsy mamorona logo
  276.                     if (!productImg) return;
  277.                     // Raha efa misy logo, tsy mamorona indray
  278.                     if (wrapper.querySelector(".logo-overlay")) return;
  279.                     // Mamorona logo
  280.                     const logo = document.createElement("img");
  281.                     logo.src = logoSrc;
  282.                     logo.alt = "Logo";
  283.                     logo.className = "logo-overlay";
  284.                     wrapper.appendChild(logo);
  285.                 });
  286.             }
  287.             // Alefa rehefa loaded ny DOM
  288.             document.addEventListener("DOMContentLoaded", addLogoToProductImages);
  289.         </script>
  290.         <script>
  291.             const productClickBaseUrl = "{{ path('app_product_click_search', {'id': 'PRODUCT_ID'}) }}";
  292.             const productShowBaseUrl  = "{{ path('app_product_show', {'id': 'PRODUCT_ID'}) }}";
  293.         </script>
  294.         <script>
  295.             document.addEventListener("DOMContentLoaded", function () {
  296.                 const input = document.getElementById("search-input");
  297.                 const suggestionsBox = document.getElementById("search-suggestions");
  298.                 const suggestionsBoxResults = document.getElementById("search-suggestions-results");
  299.                 input.addEventListener("focus", function () {
  300.                     if (this.value.length >= 2) return;
  301.                     fetch("{{ path('ajax_random_products') }}")
  302.                         .then(response => response.json())
  303.                         .then(data => {
  304.                             const categoryContainer = document.getElementById("categoryResults");
  305.                             categoryContainer.innerHTML = "";
  306.                             if (!data || data.length === 0) {
  307.                                 categoryContainer.innerHTML = `
  308.                                     <div class="text-center text-muted py-4">
  309.                                         Aucun produit disponible.
  310.                                     </div>
  311.                                 `;
  312.                             }
  313.                             (data || []).forEach(group => {
  314.                                 let block = document.createElement("div");
  315.                                 block.classList.add("mb-4");
  316.                                 // block.innerHTML = `
  317.                                 //     <div class="d-flex justify-content-between align-items-center mb-2">
  318.                                 //         <div class="fw-bold">${group.categoryName.charAt(0).toUpperCase() + group.categoryName.slice(1)}</div>
  319.                                 //         <a href="#" class="small text-primary"></a>
  320.                                 //     </div>
  321.                                 //     <div class="row g-3" id="cat-${group.categoryId}"></div>
  322.                                 // `;
  323.                                 block.innerHTML = `
  324.                                     <div class="category-header d-flex justify-content-between align-items-center mb-3">
  325.                                         <h6 class="category-title mb-0">
  326.                                             ${group.categoryName 
  327.                                                 ? group.categoryName.charAt(0).toUpperCase() + group.categoryName.slice(1) 
  328.                                                 : ''}
  329.                                         </h6>
  330.                                         <a href="#" class="small text-decoration-none fw-semibold view-all"></a>
  331.                                     </div>
  332.                                     <div class="divider mb-3"></div>
  333.                                     <div class="row g-3" id="cat-${group.categoryId}"></div>
  334.                                 `;
  335.                                 categoryContainer.appendChild(block);
  336.                                 let row = block.querySelector(`#cat-${group.categoryId}`);
  337.                                 group.products.slice(0, 4).forEach(product => {
  338.                                     let col = document.createElement("div");
  339.                                     // col.classList.add("col-3");
  340.                                     col.classList.add("col-6", "col-md-4", "col-lg-3");
  341.                                     col.innerHTML = `
  342.                                         <div class="product-item">
  343.                                             <img src="/uploads/products/${product.image}">
  344.                                             <div class="small mt-2">${product.title}</div>
  345.                                         </div>
  346.                                     `;
  347.                                     col.querySelector(".product-item").addEventListener("click", function (e) {
  348.                                         e.preventDefault();
  349.                                         let clickUrl = productClickBaseUrl.replace('PRODUCT_ID', product.id);
  350.                                         let showUrl  = productShowBaseUrl.replace('PRODUCT_ID', product.id);
  351.                                         fetch(clickUrl + "?fromSearch=1", {
  352.                                             method: "POST",
  353.                                             headers: {
  354.                                                 "X-Requested-With": "XMLHttpRequest"
  355.                                             }
  356.                                         })
  357.                                         .then(() => {
  358.                                             window.location.href = showUrl;
  359.                                         });
  360.                                     });
  361.                                     row.appendChild(col);
  362.                                 });
  363.                             });
  364.                             // suggestionsBox.style.display = "block";
  365.                             suggestionsBox.style.display = "block";
  366.                             suggestionsBox.classList.add("show");
  367.                         });
  368.                 });
  369.                 input.addEventListener("keyup", function () {
  370.                     let query = this.value;
  371.                     if (query.length < 2) {
  372.                         suggestionsBox.style.display = "none";
  373.                         return;
  374.                     }
  375.                     fetch("{{ path('ajax_search') }}?q=" + query)
  376.                         .then(response => response.json())
  377.                         .then(data => {
  378.                             suggestionsBoxResults.innerHTML = "";
  379.                             if (data.length === 0) {
  380.                                 suggestionsBoxResults.innerHTML = `
  381.                                     <div class="list-group-item text-muted">
  382.                                         Aucun produit trouvé.
  383.                                     </div>
  384.                                 `;
  385.                                 suggestionsBoxResults.style.display = "block";
  386.                                 return;
  387.                             }
  388.                            data.forEach(product => {
  389.                                 let item = document.createElement("a");
  390.                                 item.href = "#"; // prevent default link behavior
  391.                                 item.classList.add("list-group-item", "list-group-item-action");
  392.                                 item.innerHTML = `
  393.                                     <div class="d-flex align-items-center">
  394.                                         <img src="{{ asset('uploads/products/') }}${product.image}"
  395.                                             width="40"
  396.                                             height="40"
  397.                                             class="me-2 rounded">
  398.                                         <div>
  399.                                             <div>${product.title}</div>
  400.                                         </div>
  401.                                     </div>
  402.                                 `;
  403.                                 item.addEventListener("click", function (e) {
  404.                                     e.preventDefault();
  405.                                     let clickUrl = productClickBaseUrl.replace('PRODUCT_ID', product.id);
  406.                                     let showUrl  = productShowBaseUrl.replace('PRODUCT_ID', product.id);
  407.                                     fetch(clickUrl + "?fromSearch=1", {
  408.                                         method: "POST",
  409.                                         headers: {
  410.                                             "X-Requested-With": "XMLHttpRequest"
  411.                                         }
  412.                                     })
  413.                                     .then(() => {
  414.                                         window.location.href = showUrl;
  415.                                     });
  416.                                 });
  417.                                 suggestionsBoxResults.appendChild(item);
  418.                             });
  419.                             suggestionsBoxResults.style.display = "block";
  420.                         });
  421.                 });
  422.                 // Hide when clicking outside
  423.                 // document.addEventListener("click", function (e) {
  424.                 //     if (!input.contains(e.target)) {
  425.                 //         suggestionsBox.style.display = "none";
  426.                 //     }
  427.                 // });
  428.                 document.addEventListener("click", function (e) {
  429.                     if (!input.contains(e.target) && !suggestionsBox.contains(e.target)) {
  430.                         suggestionsBox.style.display = "none";
  431.                     }
  432.                 });
  433.                 // document.addEventListener("click", function (e) {
  434.                 //     if (!input.contains(e.target) && !suggestionsBoxResults.contains(e.target)) {
  435.                 //         suggestionsBoxResults.style.display = "none";
  436.                 //     }
  437.                 // });
  438.             });
  439.         </script>
  440.     </body>
  441. </html>