templates/basee.html.twig line 1

Open in your IDE?
  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4.     <meta charset="utf-8">
  5.     <meta content="width=device-width, initial-scale=1.0" name="viewport">
  6.     <title>
  7.         Gestion Stock -TuferGroupe-
  8.         {% block title %}{% endblock %}
  9.     </title>
  10.     <meta content="" name="description">
  11.     <meta content="" name="keywords">
  12.     <link href={{asset ('assets/img/ls.png')}} rel="icon">
  13.     <link href={{asset ('assets/img/ls.png')}} rel="apple-touch-icon">
  14.     <link href="https://fonts.gstatic.com" rel="preconnect">
  15.     <link href="https://fonts.googleapis.com/css?family=Open+Sans:300,300i,400,400i,600,600i,700,700i|Nunito:300,300i,400,400i,600,600i,700,700i|Poppins:300,300i,400,400i,500,500i,600,600i,700,700i" rel="stylesheet">
  16.     <link href={{asset ("assets/vendor/bootstrap/css/bootstrap.min.css")}} rel="stylesheet">
  17.     <link href={{asset ("assets/vendor/bootstrap-icons/bootstrap-icons.css")}} rel="stylesheet">
  18.     <link href={{asset ("assets/vendor/boxicons/css/boxicons.min.css")}} rel="stylesheet">
  19.     <link href={{asset ("assets/vendor/quill/quill.snow.css")}} rel="stylesheet">
  20.     <link href={{asset ("assets/vendor/quill/quill.bubble.css")}} rel="stylesheet">
  21.     <link href={{asset ("assets/vendor/remixicon/remixicon.css")}} rel="stylesheet">
  22.     <link href={{asset ("assets/vendor/simple-datatables/style.css")}} rel="stylesheet">
  23.     <link href={{asset ("assets/css/style.css")}} rel="stylesheet">
  24.     {% block stylesheets %}{% endblock %}
  25.     <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
  26.     <style>
  27.         /* Styles pour la recherche et responsive */
  28.         .search-container {
  29.             position: relative;
  30.             width: 100%;
  31.             max-width: 500px;
  32.         }
  33.         .form-control.is-invalid:focus {
  34.             border-color: #dc3545;
  35.             box-shadow: 0 0 0 0.25rem rgba(220, 53, 69, 0.25);
  36.         }
  37.         #search-error-message {
  38.             color: #dc3545;
  39.             font-size: 0.875em;
  40.             margin-top: 5px;
  41.             animation: fadeIn 0.3s ease-in;
  42.         }
  43.         @keyframes fadeIn {
  44.             from {
  45.                 opacity: 0;
  46.                 transform: translateY(-10px);
  47.             }
  48.             to {
  49.                 opacity: 1;
  50.                 transform: translateY(0);
  51.             }
  52.         }
  53.         .autocomplete-results {
  54.             position: absolute;
  55.             z-index: 1050;
  56.             width: 100%;
  57.             max-height: 300px;
  58.             overflow-y: auto;
  59.             background-color: white;
  60.             border: 1px solid #ced4da;
  61.             border-radius: 0 0 0.375rem 0.375rem;
  62.             box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15);
  63.             display: none;
  64.             top: 100%;
  65.             left: 0;
  66.             margin-top: -1px;
  67.         }
  68.         .autocomplete-item {
  69.             padding: 0.75rem 1rem;
  70.             cursor: pointer;
  71.             border-bottom: 1px solid #f0f0f0;
  72.             display: flex;
  73.             align-items: center;
  74.             transition: background-color 0.15s ease-in-out;
  75.         }
  76.         .autocomplete-item:last-child {
  77.             border-bottom: none;
  78.         }
  79.         .autocomplete-item:hover {
  80.             background-color: #f8f9fa;
  81.         }
  82.         .autocomplete-item i {
  83.             color: #6c757d;
  84.             width: 18px;
  85.             text-align: center;
  86.             margin-right: 10px;
  87.         }
  88.         #advancedSearchMenu {
  89.             width: 320px !important;
  90.             padding: 1.25rem;
  91.             border-radius: 0.5rem;
  92.             border: none;
  93.             box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15);
  94.         }
  95.         #searchForm {
  96.             position: relative;
  97.             flex: 1;
  98.             max-width: 500px;
  99.         }
  100.         #activeFilters {
  101.             margin-top: 0.5rem;
  102.             font-size: 0.85rem;
  103.             color: #6c757d;
  104.             padding: 0.375rem 0.75rem;
  105.             background-color: #f8f9fa;
  106.             border-radius: 0.375rem;
  107.             display: inline-block;
  108.             border: 1px solid #e9ecef;
  109.         }
  110.         .filter-tag {
  111.             display: inline-block;
  112.             padding: 0.25rem 0.5rem;
  113.             margin-right: 0.25rem;
  114.             background-color: #e9ecef;
  115.             border-radius: 1rem;
  116.             font-size: 0.8rem;
  117.             color: #495057;
  118.         }
  119.         .filter-tag i {
  120.             margin-left: 0.25rem;
  121.             cursor: pointer;
  122.             color: #6c757d;
  123.         }
  124.         .filter-tag i:hover {
  125.             color: #dc3545;
  126.         }
  127.         /* Header responsif */
  128.         .header {
  129.             padding: 0.75rem 1rem;
  130.         }
  131.         .header .logo img {
  132.             height: 40px;
  133.             width: auto;
  134.         }
  135.         .header-nav {
  136.             align-items: center;
  137.         }
  138.         /* Sidebar responsive */
  139.         .sidebar {
  140.             transition: all 0.3s ease;
  141.         }
  142.         .nav-item .nav-link {
  143.             padding: 0.75rem 1rem;
  144.             border-radius: 0.375rem;
  145.             margin: 0.125rem 0;
  146.             transition: all 0.15s ease-in-out;
  147.         }
  148.         .nav-item .nav-link:hover {
  149.             background-color: rgba(13, 110, 253, 0.1);
  150.             color: #0d6efd;
  151.         }
  152.         .nav-item .nav-link.active {
  153.             background-color: #0d6efd;
  154.             color: white;
  155.         }
  156.         /* Panier badge */
  157.         .badge-number {
  158.             position: absolute;
  159.             top: -8px;
  160.             right: -8px;
  161.             min-width: 18px;
  162.             height: 18px;
  163.             border-radius: 50%;
  164.             font-size: 0.75rem;
  165.             display: flex;
  166.             align-items: center;
  167.             justify-content: center;
  168.         }
  169.         /* Avatar responsive */
  170.         .avatar {
  171.             width: 38px;
  172.             height: 38px;
  173.             border-radius: 50%;
  174.             display: flex;
  175.             align-items: center;
  176.             justify-content: center;
  177.             font-weight: 600;
  178.             font-size: 0.9rem;
  179.         }
  180.         /* Responsive adjustments */
  181.         @media (max-width: 992px) {
  182.             .search-container {
  183.                 max-width: 100%;
  184.             }
  185.             
  186.             #advancedSearchMenu {
  187.                 width: 280px !important;
  188.                 padding: 1rem;
  189.             }
  190.             
  191.             .header .logo img {
  192.                 height: 35px;
  193.             }
  194.             
  195.             .avatar {
  196.                 width: 34px;
  197.                 height: 34px;
  198.                 font-size: 0.8rem;
  199.             }
  200.         }
  201.         @media (max-width: 768px) {
  202.             .header {
  203.                 padding: 0.5rem 0.75rem;
  204.             }
  205.             
  206.             .search-container {
  207.                 margin: 0.5rem 0;
  208.             }
  209.             
  210.             #advancedSearchMenu {
  211.                 width: 100% !important;
  212.                 max-width: 300px;
  213.                 left: 0 !important;
  214.                 right: 0 !important;
  215.                 margin: 0 auto;
  216.             }
  217.             
  218.             .header-nav ul {
  219.                 gap: 0.5rem;
  220.             }
  221.             
  222.             .nav-item .nav-link {
  223.                 padding: 0.5rem;
  224.             }
  225.         }
  226.         @media (max-width: 576px) {
  227.             .header .logo img {
  228.                 height: 30px;
  229.             }
  230.             
  231.             .avatar {
  232.                 width: 30px;
  233.                 height: 30px;
  234.                 font-size: 0.75rem;
  235.             }
  236.             
  237.             .search-container .input-group .form-control {
  238.                 font-size: 0.9rem;
  239.             }
  240.             
  241.             .autocomplete-item {
  242.                 padding: 0.5rem 0.75rem;
  243.                 font-size: 0.9rem;
  244.             }
  245.         }
  246.         /* Animation pour la sidebar sur mobile */
  247.         @media (max-width: 1199px) {
  248.             .sidebar {
  249.                 left: -300px;
  250.             }
  251.             
  252.             .sidebar.open {
  253.                 left: 0;
  254.             }
  255.             
  256.             .main {
  257.                 margin-left: 0;
  258.             }
  259.         }
  260.         /* Amélioration du dropdown menu */
  261.         .dropdown-menu {
  262.             border: none;
  263.             box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15);
  264.             border-radius: 0.5rem;
  265.         }
  266.         .dropdown-item {
  267.             padding: 0.5rem 1rem;
  268.             transition: all 0.15s ease-in-out;
  269.         }
  270.         .dropdown-item:hover {
  271.             background-color: #f8f9fa;
  272.             color: #0d6efd;
  273.         }
  274.         /* Style pour les formulaires dans le menu avancé */
  275.         #advancedSearchMenu .form-label {
  276.             font-weight: 600;
  277.             color: #495057;
  278.             margin-bottom: 0.5rem;
  279.         }
  280.         #advancedSearchMenu .form-select,
  281.         #advancedSearchMenu .form-control {
  282.             border-radius: 0.375rem;
  283.             border: 1px solid #ced4da;
  284.             transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
  285.         }
  286.         #advancedSearchMenu .form-select:focus,
  287.         #advancedSearchMenu .form-control:focus {
  288.             border-color: #86b7fe;
  289.             box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25);
  290.         }
  291.         /* Footer responsive */
  292.         .footer {
  293.             padding: 1rem;
  294.             text-align: center;
  295.         }
  296.         .footer .copyright,
  297.         .footer .credits {
  298.             margin: 0.5rem 0;
  299.         }
  300.         @media (max-width: 576px) {
  301.             .footer {
  302.                 font-size: 0.875rem;
  303.             }
  304.         }
  305.     </style>
  306. </head>
  307. <body>
  308.     <!-- ======= Header ======= -->
  309.     <header id="header" class="header fixed-top d-flex align-items-center">
  310.         {% if not is_granted('ROLE_LIVREUR') %}
  311.         <div class="d-flex align-items-center justify-content-between">
  312.             <a href="{{ path('app_dashboard') }}" class="logo d-flex align-items-center">
  313.                 <img src="{{ asset('assets/img/ls.png') }}" alt="logo" class="img-fluid">
  314.             </a>
  315.             <i class="bi bi-list toggle-sidebar-btn d-xl-none ms-3"></i>
  316.         </div>
  317.         {% endif %}
  318.         
  319.         {% if is_granted('ROLE_LIVREUR') %}
  320.         <div class="d-flex align-items-center justify-content-between">
  321.             <a href="{{ path('app_livreur_dashboard') }}" class="logo d-flex align-items-center">
  322.                 <img src="{{ asset('assets/img/ls.png') }}" alt="logo" class="img-fluid">
  323.             </a>
  324.             <i class="bi bi-list toggle-sidebar-btn d-xl-none ms-3"></i>
  325.         </div>
  326.         {% endif %}
  327.         <div class="d-flex justify-content-center flex-grow-1 mx-2 mx-md-3">
  328.             {% if not is_granted('ROLE_LIVREUR') %}
  329.             <div class="search-container">
  330.                 <form class="d-flex align-items-center w-100" method="GET" action="{{ path('produit_search') }}" id="searchForm">
  331.                     <div class="input-group">
  332.                         <input type="text" 
  333.                                name="search" 
  334.                                id="searchInput" 
  335.                                class="form-control" 
  336.                                placeholder="Rechercher..." 
  337.                                value="{{ app.request.query.get('search') }}" 
  338.                                autocomplete="off">
  339.                         <button class="btn btn-outline-primary" type="submit" aria-label="Rechercher">
  340.                             <i class="bi bi-search"></i>
  341.                         </button>
  342.                         <button class="btn btn-outline-secondary dropdown-toggle" 
  343.                                 type="button" 
  344.                                 id="advancedSearchButton" 
  345.                                 data-bs-toggle="dropdown" 
  346.                                 aria-expanded="false"
  347.                                 aria-label="Filtres avancés">
  348.                             <i class="bi bi-sliders"></i>
  349.                         </button>
  350.                         <div class="dropdown-menu dropdown-menu-end" id="advancedSearchMenu">
  351.                             <h6 class="dropdown-header fw-bold">Filtres avancés</h6>
  352.                             <div class="mb-3">
  353.                                 <label class="form-label">Type de recherche</label>
  354.                                 <select name="search_type" class="form-select form-select-sm">
  355.                                     <option value="all" selected>Tous</option>
  356.                                     <option value="produits">Produits</option>
  357.                                     <option value="clients">Clients</option>
  358.                                     <option value="vendeurs">Vendeurs</option>
  359.                                     <option value="boutiques">Boutiques</option>
  360.                                 </select>
  361.                             </div>
  362.                             <div class="mb-3">
  363.                                 <label class="form-label">Trier par</label>
  364.                                 <select name="sort" class="form-select form-select-sm">
  365.                                     <option value="nom">Nom</option>
  366.                                     <option value="prix_asc">Prix (croissant)</option>
  367.                                     <option value="prix_desc">Prix (décroissant)</option>
  368.                                     <option value="stock">Stock disponible</option>
  369.                                 </select>
  370.                             </div>
  371.                             <button type="submit" class="btn btn-primary btn-sm w-100">
  372.                                 <i class="bi bi-check2"></i> Appliquer
  373.                             </button>
  374.                         </div>
  375.                     </div>
  376.                 </form>
  377.                 <div id="autocompleteResults" class="autocomplete-results"></div>
  378.             </div>
  379.             {% endif %}
  380.         </div>
  381.         <nav class="header-nav ms-auto">
  382.             <ul class="d-flex align-items-center mb-0 list-unstyled">
  383.                 <li class="nav-item me-2">
  384.                     <a class="nav-link nav-icon position-relative" href="{{ path('app_panier') }}" aria-label="Panier">
  385.                         <i class="bi bi-cart-fill fs-5"></i>
  386.                         <span id="panier-badge" class="badge bg-primary badge-number"></span>
  387.                     </a>
  388.                 </li>
  389.                 {% if app.user %}
  390.                 <li class="nav-item dropdown">
  391.                     <a class="nav-link nav-profile d-flex align-items-center" 
  392.                        href="#" 
  393.                        data-bs-toggle="dropdown"
  394.                        aria-expanded="false">
  395.                         {% set firstLetter = firstLetter(app.user) %}
  396.                         <div class="avatar btn-primary d-flex align-items-center justify-content-center">
  397.                             {{ firstLetter }}
  398.                         </div>
  399.                     </a>
  400.                     <ul class="dropdown-menu dropdown-menu-end profile">
  401.                         <li class="dropdown-header text-center py-2">
  402.                             <h6 class="mb-1">{{ app.user.fullName }}</h6>
  403.                             <small class="text-muted">{{ app.user.roles[0] }}</small>
  404.                         </li>
  405.                         <li><hr class="dropdown-divider"></li>
  406.                         <li>
  407.                             <a class="dropdown-item d-flex align-items-center" href="{{ path('app_logout') }}">
  408.                                 <i class="bi bi-box-arrow-right me-2"></i>
  409.                                 <span>Déconnexion</span>
  410.                             </a>
  411.                         </li>
  412.                     </ul>
  413.                 </li>
  414.                 {% endif %}
  415.             </ul>
  416.         </nav>
  417.     </header>
  418.     <!-- End Header -->
  419.     <!-- ======= Sidebar ======= -->
  420.     {% if not is_granted('ROLE_LIVREUR') %}
  421.     <aside id="sidebar" class="sidebar">
  422.         <ul class="sidebar-nav" id="sidebar-nav">
  423.             {% if is_granted('ROLE_SUPER_ADMIN') or is_granted('ROLE_ADMIN') %}
  424.             <li class="nav-item">
  425.                 <a class="nav-link collapsed" href="{{ path('app_dashboard') }}">
  426.                     <i class="bi bi-grid me-2"></i>
  427.                     <span>Dashboard</span>
  428.                 </a>
  429.             </li>
  430.             <li class="nav-item">
  431.                 <a class="nav-link collapsed" href="{{ path('app_boutique_index') }}">
  432.                     <i class="bi bi-shop me-2"></i>
  433.                     <span>Boutiques</span>
  434.                 </a>
  435.             </li>
  436.             <li class="nav-item">
  437.                 <a class="nav-link collapsed" href="{{ path('app_entree_stock_index') }}">
  438.                     <i class="bi bi-box-arrow-in-down me-2"></i>
  439.                     <span>Entrées</span>
  440.                 </a>
  441.             </li>
  442.             <li class="nav-item">
  443.                 <a class="nav-link collapsed" href="{{ path('app_sortie_stock_index') }}">
  444.                     <i class="bi bi-box-arrow-up me-2"></i>
  445.                     <span>Sorties</span>
  446.                 </a>
  447.             </li>
  448.             <li class="nav-item">
  449.                 <a class="nav-link collapsed" href="{{ path('app_client_index') }}">
  450.                     <i class="bi bi-people me-2"></i>
  451.                     <span>Clients</span>
  452.                 </a>
  453.             </li>
  454.             <li class="nav-item">
  455.                 <a class="nav-link collapsed" href="{{ path('app_user_index') }}">
  456.                     <i class="bi bi-person-gear me-2"></i>
  457.                     <span>Utilisateurs</span>
  458.                 </a>
  459.             </li>
  460.             {% endif %}
  461.             <li class="nav-item">
  462.                 <a class="nav-link collapsed" href="{{ path('app_dashboard') }}">
  463.                     <i class="bi bi-grid me-2"></i>
  464.                     <span>Dashboard</span>
  465.                 </a>
  466.             </li>
  467.             <li class="nav-item">
  468.                 <a class="nav-link collapsed" href="{{ path('app_categorie_index') }}">
  469.                     <i class="bi bi-tags me-2"></i>
  470.                     <span>Catégories</span>
  471.                 </a>
  472.             </li>
  473.             <li class="nav-item">
  474.                 <a class="nav-link collapsed" href="{{ path('app_produit_index') }}">
  475.                     <i class="bi bi-box-seam me-2"></i>
  476.                     <span>Produits</span>
  477.                 </a>
  478.             </li>
  479.             <li class="nav-item">
  480.                 <a class="nav-link collapsed" href="{{ path('app_vente_index') }}">
  481.                     <i class="bi bi-cart me-2"></i>
  482.                     <span>Ventes</span>
  483.                 </a>
  484.             </li>
  485.             <li class="nav-item">
  486.                 <a class="nav-link collapsed" href="{{ path('app_facture_index') }}">
  487.                     <i class="bi bi-receipt me-2"></i>
  488.                     <span>Factures</span>
  489.                 </a>
  490.             </li>
  491.             <li class="nav-item">
  492.                 <a class="nav-link collapsed" href="{{ path('app_service_index') }}">
  493.                     <i class="bi bi-gear me-2"></i>
  494.                     <span>Services</span>
  495.                 </a>
  496.             </li>
  497.             <li class="nav-item">
  498.                 <a class="nav-link collapsed" href="{{ path('app_facture_service_index') }}">
  499.                     <i class="bi bi-file-earmark-text me-2"></i>
  500.                     <span>Factures Services</span>
  501.                 </a>
  502.             </li>
  503.             {% if is_granted('ROLE_SUPER_ADMIN') %}
  504.             <li class="nav-item">
  505.                 <a class="nav-link collapsed" href="{{ path('app_client_echeances') }}">
  506.                     <i class="bi bi-cash-stack me-2"></i>
  507.                     <span>Dettes Clients</span>
  508.                 </a>
  509.             </li>
  510.             <li class="nav-item">
  511.                 <a class="nav-link collapsed" href="{{ path('app_recettes') }}">
  512.                     <i class="bi bi-cash-coin me-2"></i>
  513.                     <span>Recettes</span>
  514.                 </a>
  515.             </li>
  516.             <li class="nav-item">
  517.                 <a class="nav-link collapsed" href="{{ path('app_action_log_index') }}">
  518.                     <i class="bi bi-activity me-2"></i>
  519.                     <span>Historique</span>
  520.                 </a>
  521.             </li>
  522.             {% endif %}
  523.         </ul>
  524.     </aside>
  525.     {% endif %}
  526.     <!-- End Sidebar-->
  527.     <main id="main" class="main">
  528.         <div class="pagetitle">
  529.             <h1 class="mb-2">
  530.                 {% block subtitle %}{% endblock %}
  531.             </h1>
  532.             <nav aria-label="breadcrumb">
  533.                 <ol class="breadcrumb">
  534.                     <li class="breadcrumb-item">
  535.                         <a href="{{ path('app_dashboard') }}">
  536.                             {% block subtitle2 %}Accueil{% endblock %}
  537.                         </a>
  538.                     </li>
  539.                 </ol>
  540.             </nav>
  541.         </div>
  542.         {% block body %}{% endblock %}
  543.     </main>
  544.     <!-- ======= Footer ======= -->
  545.     <footer id="footer" class="footer bg-light border-top">
  546.         <div class="container-fluid">
  547.             <div class="row">
  548.                 <div class="col-md-6">
  549.                     <div class="copyright">
  550.                         &copy; Copyright <strong><span>GestStock TuferGroupe</span></strong>. 
  551.                         Tous droits réservés
  552.                     </div>
  553.                 </div>
  554.                 <div class="col-md-6">
  555.                     <div class="credits text-md-end">
  556.                         Développé par <a href="mailto:jsias288@gmail.com" class="text-decoration-none">jsias288@gmail.com</a>
  557.                     </div>
  558.                 </div>
  559.             </div>
  560.         </div>
  561.     </footer>
  562.     <!-- End Footer -->
  563.     <a href="#" class="back-to-top d-flex align-items-center justify-content-center rounded-circle">
  564.         <i class="bi bi-arrow-up-short"></i>
  565.     </a>
  566.     <!-- Vendor JS Files -->
  567.     <script src={{asset ("assets/vendor/apexcharts/apexcharts.min.js")}}></script>
  568.     <script src={{asset ("assets/vendor/bootstrap/js/bootstrap.bundle.min.js")}}></script>
  569.     <script src={{asset ("assets/vendor/chart.js/chart.umd.js")}}></script>
  570.     <script src={{asset ("assets/vendor/echarts/echarts.min.js")}}></script>
  571.     <script src={{asset ("assets/vendor/quill/quill.min.js")}}></script>
  572.     <script src={{asset ("assets/vendor/simple-datatables/simple-datatables.js")}}></script>
  573.     <script src={{asset ("assets/vendor/tinymce/tinymce.min.js")}}></script>
  574.     <script src={{asset ("assets/vendor/php-email-form/validate.js")}}></script>
  575.     <script src={{asset ("assets/js/main.js")}}></script>
  576.     <script>
  577.         document.addEventListener('DOMContentLoaded', function() {
  578.             // Récupération des éléments
  579.             const searchInput = document.getElementById('searchInput');
  580.             const searchForm = document.getElementById('searchForm');
  581.             const advancedSearchButton = document.getElementById('advancedSearchButton');
  582.             const advancedSearchMenu = document.getElementById('advancedSearchMenu');
  583.             const autocompleteContainer = document.getElementById('autocompleteResults');
  584.             const AUTOCOMPLETE_URL = "{{ path('produit_autocomplete') }}";
  585.             
  586.             let debounceTimer;
  587.             
  588.             // VALIDATION DE LA RECHERCHE
  589.             searchForm?.addEventListener('submit', function(e) {
  590.                 const searchValue = searchInput.value.trim();
  591.                 
  592.                 if (searchValue === '' || searchValue.length < 2) {
  593.                     e.preventDefault();
  594.                     searchInput.classList.add('is-invalid');
  595.                     searchInput.focus();
  596.                     showSearchError('Veuillez saisir au moins 2 caractères pour effectuer une recherche.');
  597.                     return false;
  598.                 }
  599.                 
  600.                 searchInput.classList.remove('is-invalid');
  601.             });
  602.             
  603.             // Fonction pour afficher un message d'erreur
  604.             function showSearchError(message) {
  605.                 const existingError = document.getElementById('search-error-message');
  606.                 if (existingError) {
  607.                     existingError.remove();
  608.                 }
  609.                 
  610.                 const errorDiv = document.createElement('div');
  611.                 errorDiv.id = 'search-error-message';
  612.                 errorDiv.className = 'invalid-feedback d-block';
  613.                 errorDiv.textContent = message;
  614.                 
  615.                 searchInput.parentNode.insertBefore(errorDiv, searchInput.nextSibling);
  616.                 
  617.                 setTimeout(() => {
  618.                     if (errorDiv && errorDiv.parentNode) {
  619.                         errorDiv.remove();
  620.                     }
  621.                 }, 3000);
  622.             }
  623.             
  624.             // Autocomplétion
  625.             searchInput?.addEventListener('input', function() {
  626.                 const searchValue = this.value.trim();
  627.                 
  628.                 if (searchValue.length >= 2) {
  629.                     this.classList.remove('is-invalid');
  630.                     const errorMessage = document.getElementById('search-error-message');
  631.                     if (errorMessage) {
  632.                         errorMessage.remove();
  633.                     }
  634.                 }
  635.                 
  636.                 clearTimeout(debounceTimer);
  637.                 
  638.                 if (this.value.length < 2) {
  639.                     autocompleteContainer.innerHTML = '';
  640.                     autocompleteContainer.style.display = 'none';
  641.                     return;
  642.                 }
  643.                 
  644.                 debounceTimer = setTimeout(() => {
  645.                     fetch(`${AUTOCOMPLETE_URL}?q=${encodeURIComponent(this.value)}`)
  646.                         .then(response => response.json())
  647.                         .then(data => {
  648.                             autocompleteContainer.innerHTML = '';
  649.                             
  650.                             if (data.length > 0) {
  651.                                 autocompleteContainer.style.display = 'block';
  652.                                 
  653.                                 data.forEach(item => {
  654.                                     const suggestionElement = document.createElement('div');
  655.                                     suggestionElement.className = 'autocomplete-item';
  656.                                     
  657.                                     let icon = '';
  658.                                     switch(item.type) {
  659.                                         case 'produit':
  660.                                             icon = '<i class="bi bi-box me-2"></i>';
  661.                                             break;
  662.                                         case 'client':
  663.                                             icon = '<i class="bi bi-person me-2"></i>';
  664.                                             break;
  665.                                         case 'vendeur':
  666.                                             icon = '<i class="bi bi-person-badge me-2"></i>';
  667.                                             break;
  668.                                         case 'boutique':
  669.                                             icon = '<i class="bi bi-shop me-2"></i>';
  670.                                             break;
  671.                                     }
  672.                                     
  673.                                     suggestionElement.innerHTML = `${icon} <span>${item.value}</span>`;
  674.                                     
  675.                                     suggestionElement.addEventListener('click', function() {
  676.                                         searchInput.value = item.value;
  677.                                         autocompleteContainer.style.display = 'none';
  678.                                         searchInput.classList.remove('is-invalid');
  679.                                         
  680.                                         const searchTypeSelect = document.querySelector('select[name="search_type"]');
  681.                                         if (searchTypeSelect && item.type) {
  682.                                             let filterType;
  683.                                             switch(item.type) {
  684.                                                 case 'produit': filterType = 'produits'; break;
  685.                                                 case 'client': filterType = 'clients'; break;
  686.                                                 case 'vendeur': filterType = 'vendeurs'; break;
  687.                                                 case 'boutique': filterType = 'boutiques'; break;
  688.                                                 default: filterType = 'all';
  689.                                             }
  690.                                             searchTypeSelect.value = filterType;
  691.                                         }
  692.                                         
  693.                                         searchForm.submit();
  694.                                     });
  695.                                     
  696.                                     autocompleteContainer.appendChild(suggestionElement);
  697.                                 });
  698.                             } else {
  699.                                 autocompleteContainer.style.display = 'none';
  700.                             }
  701.                         })
  702.                         .catch(error => {
  703.                             console.error('Erreur lors de la récupération des suggestions:', error);
  704.                         });
  705.                 }, 300);
  706.             });
  707.             
  708.             // Masquer les résultats d'autocomplétion lors d'un clic ailleurs
  709.             document.addEventListener('click', function(e) {
  710.                 if (e.target !== searchInput && !autocompleteContainer.contains(e.target)) {
  711.                     autocompleteContainer.style.display = 'none';
  712.                 }
  713.             });
  714.             
  715.             // Empêcher la fermeture du menu avancé lors d'un clic à l'intérieur
  716.             advancedSearchMenu?.addEventListener('click', function(e) {
  717.                 e.stopPropagation();
  718.             });
  719.         });
  720.         // Fonctions existantes pour le panier et les données
  721.         $(document).ready(function() {
  722.             $(".add-to-cart").click(function(e) {
  723.                 e.preventDefault();
  724.                 var url = $(this).attr("href");
  725.                 $.ajax({
  726.                     url: url,
  727.                     method: "GET",
  728.                     success: function(response) {
  729.                         if (response === 'error') {
  730.                             alert('La quantité en stock est inférieure ou égale à la quantité d\'alerte. Impossible d\'ajouter le produit.');
  731.                         } else {
  732.                             $("#panier-content").html(response);
  733.                             updatePanierBadge();
  734.                         }
  735.                     }
  736.                 });
  737.             });
  738.         });
  739.         function updatePanierBadge() {
  740. $.ajax({
  741. url: "{{ path('count_panier') }}",
  742. method: "GET",
  743. success: function (response) {
  744. $("#panier-badge").text(response.panierSize);
  745. }
  746. });
  747. }
  748. updatePanierBadge();
  749. document.addEventListener('DOMContentLoaded', function () {
  750. function updateSalesData(filter) {
  751. fetch('{{ path('app_dashboard_data') }}?filter=' + filter).then(response => response.json()).then(data => {
  752. const salesCountElement = document.querySelector('#sales-count');
  753. const salesTitleElement = document.querySelector('#sales-title');
  754. salesCountElement.textContent = data.nombreVentes;
  755. switch (filter) {
  756. case 'today': salesTitleElement.textContent = 'Ventes | Aujourd\'hui';
  757. break;
  758. case 'month': salesTitleElement.textContent = 'Ventes | Ce mois-ci';
  759. break;
  760. case 'year': salesTitleElement.textContent = 'Ventes | Cette année';
  761. break;
  762. default: salesTitleElement.textContent = 'Ventes | Aujourd\'hui';
  763. break;
  764. }
  765. }).catch(error => console.error('Error:', error));
  766. }
  767. const dropdownItems = document.querySelectorAll('.sales-dropdown-item');
  768. dropdownItems.forEach(item => {
  769. item.addEventListener('click', function (event) {
  770. event.preventDefault();
  771. const filter = this.getAttribute('data-filter');
  772. updateSalesData(filter);
  773. });
  774. });
  775. updateSalesData('today');
  776. function updateRevenueData(filter) {
  777. fetch('{{ path('app_dashboard_data2') }}?filter=' + filter).then(response => response.json()).then(data => {
  778. const revenueCountElement = document.querySelector('#revenue-count');
  779. const revenueTitleElement = document.querySelector('#revenue-title');
  780. revenueCountElement.textContent = data.totalRevenue.toFixed(2);
  781. switch (filter) {
  782. case 'today': revenueTitleElement.textContent = 'Revenue |  Aujourd\'hui';
  783. break;
  784. case 'month': revenueTitleElement.textContent = 'Revenue | Ce mois-ci';
  785. break;
  786. case 'year': revenueTitleElement.textContent = 'Revenue | Cette année';
  787. break;
  788. default: revenueTitleElement.textContent = 'Revenue |  Aujourd\'hui';
  789. break;
  790. }
  791. }).catch(error => console.error('Error:', error));
  792. }
  793. const revenueDropdownItems = document.querySelectorAll('.revenue-dropdown-item');
  794. revenueDropdownItems.forEach(item => {
  795. item.addEventListener('click', function (event) {
  796. event.preventDefault();
  797. const filter = this.getAttribute('data-filter');
  798. updateRevenueData(filter);
  799. });
  800. });
  801. updateRevenueData('today');
  802. });
  803.             </script>
  804.         {% block javascripts %}{% endblock %}
  805.         </body>
  806.     </html>