templates/admin/recette.html.twig line 1

Open in your IDE?
  1. {% extends 'sidebar.twig' %}
  2. {% block title %}
  3.     IMMMY BEAUTY - Recettes
  4. {% endblock %}
  5. {% block stylesheets %}
  6.     <link rel="stylesheet" href="{{ asset('css/style.css') }}">
  7.     <link href="https://cdn.jsdelivr.net/npm/tailwindcss@2.2.19/dist/tailwind.min.css" rel="stylesheet">
  8.     <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/flatpickr/dist/flatpickr.min.css">
  9. {% endblock %}
  10. {% block page_emoji %}
  11.     đź“‹â€Ť
  12. {% endblock %}
  13. {% block page_titre %}
  14.     Recettes
  15. {% endblock %}
  16. {% block page_info %}
  17.     GĂ©rez vos recettes ici.
  18. {% endblock %}
  19. {% block admin_content %}
  20.     <div class="container">
  21.         <div class="flex flex-wrap justify-center mb-4"> <!-- Ajouter une marge infĂ©rieure -->
  22.             <div id="dateDisplay" class="text-3xl text-center font-bold cursor-pointer color-black" onclick="openCalendar()">
  23.             </div>
  24.             <label for="uniqueHiddenDateInput"></label><input type="text" id="uniqueHiddenDateInput" class="hidden" />
  25.         </div>
  26.         <div class="flex flex-wrap">
  27.             <div class="w-full lg:w-1/2 p-4 bg-white rounded shadow">
  28.                 <h2 class="text-xl font-bold mb-4 text-center">Ajouter une recette</h2>
  29.                 <form id="recetteForm" class="space-y-4">
  30.                     {% set urlDate = app.request.query.get('date') %}
  31.                     <input type="hidden" id="dateInput" name="date" value="{{ urlDate ? urlDate : "now"|date('d/m/Y') }}">
  32.                     <div id="prestationsContainer">
  33.                         <div class="prestation-group mb-4 flex items-center space-x-4">
  34.                             <div class="flex-grow">
  35.                                 <label for="categorie_0" class="block text-gray-700">CatĂ©gorie <span style="color: red;">*</span></label>
  36.                                 <select name="prestations[0][categorie_id]" id="categorie_0" class="categorie-select mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50" required>
  37.                                     <option value="">SĂ©lectionnez une catĂ©gorie</option>
  38.                                     {% for categorie in categories %}
  39.                                         <option value="{{ categorie.id }}">{{ categorie.nom }}</option>
  40.                                     {% endfor %}
  41.                                 </select>
  42.                             </div>
  43.                             <div class="hidden prestation-select-group mt-4 flex-grow">
  44.                                 <label for="prestation_0" class="block text-gray-700">Prestation <span style="color: red;">*</span></label>
  45.                                 <select name="prestations[0][prestation_id]" id="prestation_0" class="prestation-select mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50" required>
  46.                                     <option value="">SĂ©lectionnez une prestation</option>
  47.                                 </select>
  48.                             </div>
  49.                             <button type="button" class="remove-prestation ml-2 text-red-500 hover:text-red-700 hidden">
  50.                                 <i class="fas fa-times"></i>
  51.                             </button>
  52.                         </div>
  53.                     </div>
  54.                     <a href="#" id="addPrestationLink" class="mt-4 text-black-500 hover:text-gray-600-700">
  55.                         <i class="fas fa-plus"></i> Ajouter une prestation
  56.                     </a>
  57.                     <div>
  58.                         <label for="nomClient" class="block text-gray-700">Nom de la cliente <span style="color: red;">*</span></label>
  59.                         <input type="text" id="nomClient" name="nom_client" class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50" required>
  60.                     </div>
  61.                     <div>
  62.                         <label for="acompte" class="block text-gray-700">Acompte</label>
  63.                         <input type="number" step="0.01" min="0" id="acompte" name="acompte" class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50">
  64.                     </div>
  65.                     <div>
  66.                         <a href="#"
  67.                            id="discountLabel"
  68.                            class="mt-4 text-black-500 hover:text-gray-600-700"
  69.                            onclick="showDiscountInput()"
  70.                         >
  71.                             <i class="fas fa-percent"></i> RĂ©duction ?
  72.                         </a>
  73.                     </div>
  74.                     <div id="discountInputContainer" class="flex items-center space-x-4" style="display:none;">
  75.                         <label for="prixPayeSurPlace" class="block text-gray-700 flex-grow">Prix payĂ© sur place</label>
  76.                         <div class="flex items-center space-x-2 flex-grow">
  77.                             <input type="number" id="prixPayeSurPlace" step="0.01" min="0" name="prix_paye_sur_place" class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50">
  78.                             <button type="button" id="removeDiscountInput" class="ml-2 text-red-500 hover:text-red-700">
  79.                                 <i class="fas fa-times"></i>
  80.                             </button>
  81.                         </div>
  82.                     </div>
  83.                     <div>
  84.                         <label for="typePaiement" class="block text-gray-700">Type de paiement <span style="color: red;">*</span></label>
  85.                         <select name="type_paiement_id" id="typePaiement" class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50" required>
  86.                             <option value="">SĂ©lectionnez un type de paiement</option>
  87.                             <option value="1">Espèce</option>
  88.                             <option value="2">Carte Bancaire</option>
  89.                             <option value="3">Espèce & Carte bancaire</option>
  90.                         </select>
  91.                     </div>
  92.                     <div id="paiementDetails" class="mt-4" style="display: none;">
  93.                         <div id="especeContainer" class="flex items-center space-x-4" style="display: none;">
  94.                             <div class="flex-grow">
  95.                                 <label for="montantEspece" class="block text-gray-700">Montant Espèce <span style="color: red;">*</span></label>
  96.                                 <input type="number" id="montantEspece" name="montant_espece" step="0.01" min="0" class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50" required>
  97.                             </div>
  98.                         </div>
  99.                         <div id="carteBancaireContainer" class="flex items-center space-x-4" style="display: none;">
  100.                             <div class="flex-grow">
  101.                                 <label for="montantCarteBancaire" class="block text-gray-700">Montant Carte Bancaire <span style="color: red;">*</span></label>
  102.                                 <input type="number" id="montantCarteBancaire" name="montant_carte_bancaire" step="0.01" min="0" class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50" required>
  103.                             </div>
  104.                         </div>
  105.                     </div>
  106.                     <div class="text-center">
  107.                         <button class="inline-flex items-center px-4 py-2 btn btn-primary rounded-lg shadow-md transition duration-300 ease-in-out">
  108.                             Ajouter
  109.                         </button>
  110.                     </div>
  111.                 </form>
  112.             </div>
  113.             <div class="w-full lg:w-1/2 p-4 bg-white rounded shadow" id="recettesContainer" style="{{ recettes ? 'display:block;' : 'display:none;' }}">
  114.                 <h2 class="text-xl font-bold mb-4 text-center">Recette du jour</h2>
  115.                 <div class="overflow-x-auto">
  116.                     <table class="min-w-full divide-y divide-gray-200" id="recettesTable">
  117.                         <thead class="bg-gray-50">
  118.                         <tr>
  119.                             <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Cliente</th>
  120.                             <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Prestation</th>
  121.                             <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Acompte</th>
  122.                             <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">PayĂ©</th>
  123.                             <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Type de règlement</th>
  124.                             <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Total</th>
  125.                             <th></th>
  126.                         </tr>
  127.                         </thead>
  128.                         <tbody class="bg-white divide-y divide-gray-200">
  129.                         {% for recette in recettes %}
  130.                             <tr data-id="{{ recette.id }}">
  131.                                 <td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">{{ recette.nomClient }}</td>
  132.                                 <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
  133.                                     <ul>
  134.                                         {% for prestation in recette.prestations %}
  135.                                             <li>{{ prestation.nom }} <strong>({{ prestation.prix}} â‚¬)</strong></li>
  136.                                         {% endfor %}
  137.                                     </ul>
  138.                                 </td>
  139.                                 <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
  140.                                     {% if recette.acompte %} {{ recette.acompte }} â‚¬ {% else %} - {% endif %}
  141.                                 </td>
  142.                                 <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
  143.                                     <ul>
  144.                                         {% for paiement in recette.paiements %}
  145.                                             <li>{% if paiement.sommePayee %} {{ paiement.sommePayee }} â‚¬ {% else %} - {% endif %}</li>
  146.                                         {% endfor %}
  147.                                     </ul>
  148.                                 </td>
  149.                                 <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
  150.                                     <ul>
  151.                                         {% for paiement in recette.paiements %}
  152.                                             <li>{{ paiement.typePaiement.nom }}</li>
  153.                                         {% endfor %}
  154.                                     </ul>
  155.                                 </td>
  156.                                 <td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">
  157.                                     {% set totalPaye = 0 %}
  158.                                     {% if recette.acompte %}
  159.                                         {% set totalPaye = totalPaye + recette.acompte %}
  160.                                     {% endif %}
  161.                                     {% for paiement in recette.paiements %}
  162.                                         {% if paiement.sommePayee %}
  163.                                             {% set totalPaye = totalPaye + paiement.sommePayee %}
  164.                                         {% endif %}
  165.                                     {% endfor %}
  166.                                     {{ totalPaye }} â‚¬
  167.                                 </td>
  168.                                 <td>
  169.                                     <button
  170.                                             class="relative h-10 max-h-[40px] w-10 max-w-[40px] select-none rounded-lg
  171.                          bg-gray-900 text-center align-middle font-sans text-xs font-medium uppercase
  172.                           text-white shadow-md shadow-gray-900/10 transition-all hover:shadow-lg
  173.                            hover:shadow-gray-900/20 focus:opacity-[0.85] focus:shadow-none
  174.                             active:opacity-[0.85] active:shadow-none disabled:pointer-events-none
  175.                              disabled:opacity-50 disabled:shadow-none"
  176.                                             type="button"
  177.                                             data-ripple-light="true"
  178.                                             data-modal-target="deleteModal"
  179.                                             data-modal-toggle="deleteModal"
  180.                                             onclick="showDeleteModal({{ recette.id }})"
  181.                                     >
  182.                 <span class="absolute transform -translate-x-1/2 -translate-y-1/2 top-1/2 left-1/2">
  183.                     <i class="fas fa-trash" aria-hidden="true"></i>
  184.                 </span>
  185.                                     </button>
  186.                                 </td>
  187.                             </tr>
  188.                         {% endfor %}
  189.                         </tbody>
  190.                     </table>
  191.                 </div>
  192.                 <div class="overflow-x-auto mt-8">
  193.                     <table class="min-w-full divide-y divide-gray-200">
  194.                         <thead class="bg-gray-50">
  195.                         <tr>
  196.                             <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Espece</th>
  197.                             <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Carte Bancaire</th>
  198.                             <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Planity</th>
  199.                         </tr>
  200.                         </thead>
  201.                         <tbody class="bg-white divide-y divide-gray-200">
  202.                         <tr>
  203.                             <td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900" id="totalEspece">{{ totalEspece }} â‚¬</td>
  204.                             <td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900" id="totalCarteBancaire">{{ totalCarteBancaire }} â‚¬</td>
  205.                             <td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900" id="totalAcompte">{{ totalAcompte }} â‚¬</td>
  206.                         </tr>
  207.                         </tbody>
  208.                     </table>
  209.                 </div>
  210.                 <div class="px-6 py-4 bg-gray-50 sm:px-6">
  211.                     <div class="flex items-center justify-between">
  212.                         <dl class="flex items-baseline">
  213.                             <dt class="sr-only">Total</dt>
  214.                             <dd class="text-lg font-bold text-gray-900" id="totalJour">Total : {{ totalJour }} â‚¬</dd>
  215.                         </dl>
  216.                         <button id="sendRecette"
  217.                                 class="inline-flex items-center px-4 py-2 btn btn-primary rounded-lg shadow-md transition duration-300 ease-in-out text-sm sm:text-base">
  218.                             <svg class="w-6 h-6 mr-2" fill="currentColor" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
  219.                                 <path d="M12 0C5.373 0 0 5.373 0 12s5.373 12 12 12 12-5.373 12-12S18.627 0 12 0zm5.562 8.131l-1.844 8.688c-.137.646-.518.816-1.05.508l-2.904-2.14-1.402 1.35c-.156.156-.285.285-.588.285l.209-2.963 5.396-4.875c.234-.205-.052-.32-.363-.115l-6.674 4.201-2.877-.9c-.625-.195-.637-.624.131-.924l11.223-4.328c.521-.193.974.121.742 1.213z"/>
  220.                             </svg>
  221.                             Envoyer la recette
  222.                         </button>
  223.                     </div>
  224.                 </div>
  225.             </div>
  226.             <div id="deleteModal" tabindex="-1" class="hidden overflow-y-auto overflow-x-hidden fixed inset-0 z-50 w-full h-full flex justify-center items-center">
  227.                 <div class="relative p-4 w-full max-w-md mx-auto">
  228.                     <div class="relative bg-white rounded-lg shadow dark:bg-gray-700">
  229.                         <button type="button" class="absolute top-3 end-2.5 text-gray-400 bg-transparent hover:bg-gray-200 hover:text-gray-900 rounded-lg text-sm w-8 h-8 ms-auto inline-flex justify-center items-center dark:hover:bg-gray-600 dark:hover:text-white" data-modal-hide="deleteModal">
  230.                             <svg class="w-3 h-3" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 14 14">
  231.                                 <path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m1 1 6 6m0 0 6 6M7 7l6-6M7 7l-6 6"/>
  232.                             </svg>
  233.                             <span class="sr-only">Fermer</span>
  234.                         </button>
  235.                         <div class="p-4 md:p-5 text-center">
  236.                             <svg class="mx-auto mb-4 text-gray-400 w-12 h-12 dark:text-gray-200" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 20 20">
  237.                                 <path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 11V6m0 8h.01M19 10a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z"/>
  238.                             </svg>
  239.                             <h3 class="mb-5 text-lg font-normal text-gray-500 dark:text-gray-400">ĂŠtes-vous sĂ»r de vouloir supprimer cette prestation ?</h3>
  240.                             <button id="confirmDelete" type="button" class="text-white bg-red-600 hover:bg-red-800 focus:ring-4 focus:outline-none focus:ring-red-300 dark:focus:ring-red-800 font-medium rounded-lg text-sm inline-flex items-center px-5 py-2.5 text-center">
  241.                                 Oui, je suis sĂ»r
  242.                             </button>
  243.                             <button data-modal-hide="deleteModal" type="button" class="py-2.5 px-5 ms-3 text-sm font-medium text-gray-900 focus:outline-none bg-white rounded-lg border border-gray-200 hover:bg-gray-100 hover:text-blue-700 focus:z-10 focus:ring-4 focus:ring-gray-100 dark:focus:ring-gray-700 dark:bg-gray-800 dark:text-gray-400 dark:border-gray-600 dark:hover:text-white dark:hover:bg-gray-700">Non, annuler</button>
  244.                         </div>
  245.                     </div>
  246.                 </div>
  247.             </div>
  248.     </div>
  249. {% endblock %}
  250. {% block javascripts %}
  251.     {{ parent() }}
  252.     <script src="https://unpkg.com/@material-tailwind/html@latest/scripts/ripple.js"></script>
  253.     <script src="https://cdn.jsdelivr.net/npm/moment@2.29.1/moment.min.js"></script>
  254.     <script src="https://cdn.jsdelivr.net/npm/flatpickr"></script>
  255.     <script>
  256.         document.addEventListener('DOMContentLoaded', function () {
  257.             initDatePicker();
  258.             initCategorieSelect();
  259.             initRecetteForm();
  260.             document.getElementById('addPrestationLink').addEventListener('click', addPrestation);
  261.             document.getElementById('removeDiscountInput').addEventListener('click', removeDiscountInput);
  262.             document.getElementById('typePaiement').addEventListener('change', handleTypePaiementChange);
  263.         });
  264.         function handleTypePaiementChange() {
  265.             const typePaiement = this.value;
  266.             const montantEspece = document.getElementById('montantEspece');
  267.             const montantCarteBancaire = document.getElementById('montantCarteBancaire');
  268.             const paiementDetails = document.getElementById('paiementDetails');
  269.             const especeContainer = document.getElementById('especeContainer');
  270.             const carteBancaireContainer = document.getElementById('carteBancaireContainer');
  271.             if (typePaiement === '3') {
  272.                 paiementDetails.style.display = 'block';
  273.                 especeContainer.style.display = 'flex';
  274.                 carteBancaireContainer.style.display = 'flex';
  275.                 montantEspece.required = true;
  276.                 montantCarteBancaire.required = true;
  277.             } else {
  278.                 paiementDetails.style.display = 'none';
  279.                 especeContainer.style.display = 'none';
  280.                 carteBancaireContainer.style.display = 'none';
  281.                 montantEspece.required = false;
  282.                 montantCarteBancaire.required = false;
  283.                 montantEspece.value = ''; // RĂ©initialiser la valeur
  284.                 montantCarteBancaire.value = ''; // RĂ©initialiser la valeur
  285.             }
  286.         }
  287.         let prestationIndex = 1;
  288.         function addPrestation(event) {
  289.             event.preventDefault();
  290.             const prestationsContainer = document.getElementById('prestationsContainer');
  291.             const newPrestationGroup = document.createElement('div');
  292.             newPrestationGroup.classList.add('prestation-group', 'mb-4', 'flex', 'items-center');
  293.             newPrestationGroup.innerHTML = `
  294.                 <div class="flex-grow">
  295.                     <label for="categorie_${prestationIndex}" class="block text-gray-700">CatĂ©gorie <span style="color: red;">*</span></label>
  296.                     <select name="prestations[${prestationIndex}][categorie_id]" id="categorie_${prestationIndex}" class="categorie-select mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50" required>
  297.                         <option value="">SĂ©lectionnez une catĂ©gorie</option>
  298.                         {% for categorie in categories %}
  299.                             <option value="{{ categorie.id }}">{{ categorie.nom }}</option>
  300.                         {% endfor %}
  301.                     </select>
  302.                 </div>
  303.                 <div class="hidden prestation-select-group mt-4 flex-grow">
  304.                     <label for="prestation_${prestationIndex}" class="block text-gray-700">Prestation <span style="color: red;">*</span></label>
  305.                     <select name="prestations[${prestationIndex}][prestation_id]" id="prestation_${prestationIndex}" class="prestation-select mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50" required>
  306.                         <option value="">SĂ©lectionnez une prestation</option>
  307.                     </select>
  308.                 </div>
  309.                 <button type="button" class="remove-prestation ml-2 text-red-500 hover:text-red-700">
  310.                     <i class="fas fa-times"></i>
  311.                 </button>
  312.             `;
  313.             prestationsContainer.appendChild(newPrestationGroup);
  314.             prestationIndex++;
  315.             initCategorieSelect();
  316.             initRemovePrestation();
  317.         }
  318.         document.getElementById('sendRecette').addEventListener('click', function() {
  319.             const sendButton = document.getElementById('sendRecette');
  320.             sendButton.disabled = true;
  321.             fetch('/admin/recette/send', {
  322.                 method: 'POST',
  323.                 headers: {
  324.                     'Content-Type': 'application/json',
  325.                 },
  326.                 body: JSON.stringify({
  327.                     date: document.getElementById('dateInput').value
  328.                 })
  329.             })
  330.                 .then(response => response.json())
  331.                 .then(data => {
  332.                     if (data.status === 'Message envoyĂ©') {
  333.                         toastr.success('La recette a bien Ă©tĂ© envoyĂ©e', 'Succès');
  334.                     } else {
  335.                         toastr.error('Erreur lors de l\'envoi de la recette', 'Erreur');
  336.                     }
  337.                 })
  338.                 .catch(error => {
  339.                     console.error('Erreur:', error);
  340.                     toastr.error('Erreur lors de l\'envoi de la recette', 'Erreur');
  341.                 })
  342.                 .finally(() => {
  343.                     sendButton.disabled = false;
  344.                 });
  345.         });
  346.         let recetteIdToDelete;
  347.         function showDeleteModal(id) {
  348.             recetteIdToDelete = id;
  349.             const modal = document.getElementById('deleteModal');
  350.             modal.classList.remove('hidden');
  351.             modal.classList.add('flex');
  352.         }
  353.         function hideDeleteModal() {
  354.             const modal = document.getElementById('deleteModal');
  355.             modal.classList.add('hidden');
  356.             modal.classList.remove('flex');
  357.         }
  358.         function deleteRecette() {
  359.             fetch(`/admin/delete/recette/${recetteIdToDelete}`, {
  360.                 method: 'DELETE'
  361.             }).then(response => {
  362.                 console.log('RĂ©ponse brute:', response);
  363.                 return response.text();
  364.             }).then(text => {
  365.                 console.log('Texte de la rĂ©ponse:', text);
  366.                 return JSON.parse(text);
  367.             }).then(data => {
  368.                 if (data.status === 'success') {
  369.                     const row = document.querySelector(`tr[data-id="${recetteIdToDelete}"]`);
  370.                     if (row) row.remove();
  371.                     hideDeleteModal();
  372.                     updateTotals(data.totals);
  373.                     checkTableEmpty();
  374.                 } else {
  375.                     throw new Error(data.error || 'Erreur inconnue');
  376.                 }
  377.             }).catch(error => {
  378.                 console.error('Erreur:', error);
  379.                 alert('Une erreur est survenue lors de la suppression');
  380.             });
  381.         }
  382.         document.addEventListener('DOMContentLoaded', function() {
  383.             document.getElementById('confirmDelete').addEventListener('click', deleteRecette);
  384.             document.querySelectorAll('[data-modal-hide="deleteModal"]').forEach(button => {
  385.                 button.addEventListener('click', hideDeleteModal);
  386.             });
  387.         });
  388.         document.addEventListener('DOMContentLoaded', function() {
  389.             document.getElementById('confirmDelete').addEventListener('click', deleteRecette);
  390.         });
  391.         function updateTotals(totals) {
  392.             document.getElementById('totalJour').textContent = `Total : ${totals.totalJour} â‚¬`;
  393.             document.getElementById('totalEspece').textContent = `${totals.totalEspece} â‚¬`;
  394.             document.getElementById('totalCarteBancaire').textContent = `${totals.totalCarteBancaire} â‚¬`;
  395.             document.getElementById('totalAcompte').textContent = `${totals.totalAcompte} â‚¬`;
  396.         }
  397.         function checkTableEmpty() {
  398.             const table = document.getElementById('recettesTable').getElementsByTagName('tbody')[0];
  399.             if (table.rows.length === 0) {
  400.                 document.getElementById('recettesContainer').style.display = 'none';
  401.             }
  402.         }
  403.         function initCategorieSelect() {
  404.             document.querySelectorAll('.categorie-select').forEach(select => {
  405.                 select.addEventListener('change', function () {
  406.                     const selectedCategoryId = parseInt(this.value);
  407.                     const prestationSelectGroup = this.closest('.prestation-group').querySelector('.prestation-select-group');
  408.                     const prestationSelect = prestationSelectGroup.querySelector('.prestation-select');
  409.                     prestationSelect.innerHTML = '<option value="">SĂ©lectionnez une prestation</option>';
  410.                     fetch(`/admin/prestations/${selectedCategoryId}`)
  411.                         .then(response => response.json())
  412.                         .then(data => {
  413.                             if (data.length > 0) {
  414.                                 prestationSelectGroup.classList.remove('hidden');
  415.                                 data.forEach(prestation => {
  416.                                     const option = document.createElement('option');
  417.                                     option.value = prestation.id;
  418.                                     option.textContent = `${prestation.nom} (${prestation.prix}€)`;
  419.                                     prestationSelect.appendChild(option);
  420.                                 });
  421.                             } else {
  422.                                 prestationSelectGroup.classList.add('hidden');
  423.                             }
  424.                         })
  425.                         .catch(error => console.error('Erreur lors de la rĂ©cupĂ©ration des prestations:', error));
  426.                 });
  427.             });
  428.         }
  429.         function initRemovePrestation() {
  430.             document.querySelectorAll('.remove-prestation').forEach(button => {
  431.                 button.addEventListener('click', function () {
  432.                     this.closest('.prestation-group').remove();
  433.                 });
  434.             });
  435.         }
  436.         function removeDiscountInput() {
  437.             document.getElementById('discountInputContainer').style.display = 'none';
  438.         }
  439.         function initRemovePaiement() {
  440.             document.querySelectorAll('.remove-paiement').forEach(button => {
  441.                 button.addEventListener('click', function () {
  442.                     this.closest('.paiement-group').remove();
  443.                 });
  444.             });
  445.         }
  446.         function initRecetteForm() {
  447.             document.getElementById('recetteForm').addEventListener('submit', function(event) {
  448.                 event.preventDefault();
  449.                 const formData = new FormData(this);
  450.                 const nomClient = formData.get('nom_client');
  451.                 const acompte = parseFloat(formData.get('acompte'));
  452.                 const prixPayeSurPlace = parseFloat(formData.get('prix_paye_sur_place'));
  453.                 const date = formData.get('date');
  454.                 const prestations = [];
  455.                 document.querySelectorAll('.prestation-group').forEach((group, index) => {
  456.                     const categorieId = group.querySelector('.categorie-select').value;
  457.                     const prestationId = group.querySelector('.prestation-select').value;
  458.                     prestations.push({ categorie_id: categorieId, prestation_id: prestationId });
  459.                 });
  460.                 const typePaiementId = formData.get('type_paiement_id');
  461.                 const resteAPayer = parseFloat(formData.get('reste_a_payer'));
  462.                 const montantEspece = parseFloat(formData.get('montant_espece'));
  463.                 const montantCarteBancaire = parseFloat(formData.get('montant_carte_bancaire'));
  464.                 const data = {
  465.                     nomClient: nomClient,
  466.                     date: date,
  467.                     prestations: prestations,
  468.                     typePaiementId: typePaiementId,
  469.                 };
  470.                 if (acompte) {
  471.                     data.acompte = acompte;
  472.                 }
  473.                 if (prixPayeSurPlace) {
  474.                     data.prixPayeSurPlace = prixPayeSurPlace;
  475.                 }
  476.                 if (resteAPayer) {
  477.                     data.resteAPayer = resteAPayer;
  478.                     data.sommePayee = resteAPayer;
  479.                 }
  480.                 if (typePaiementId === '3') {
  481.                     if (!montantEspece && !montantCarteBancaire) {
  482.                         toastr.error('Veuillez renseigner les montants pour les paiements en espèce et carte bancaire', 'Erreur');
  483.                         return;
  484.                     }
  485.                     if (resteAPayer && montantEspece + montantCarteBancaire !== resteAPayer) {
  486.                         toastr.error('La somme des montants des paiements en espèce et carte bancaire doit ĂŞtre Ă©gale au montant payĂ© sur place', 'Erreur');
  487.                         return;
  488.                     }
  489.                     data.montantEspece = montantEspece || 0;
  490.                     data.montantCarteBancaire = montantCarteBancaire || 0;
  491.                     data.sommePayee = (montantEspece || 0) + (montantCarteBancaire || 0) + (acompte || 0);
  492.                 }
  493.                 fetch('/admin/recette/ajax', {
  494.                     method: 'POST',
  495.                     headers: {
  496.                         'Content-Type': 'application/json',
  497.                     },
  498.                     body: JSON.stringify(data)
  499.                 })
  500.                     .then(response => response.json())
  501.                     .then(data => {
  502.                         if (data.status === 'success') {
  503.                             updateTable(data.recette, data.totals);
  504.                         } else {
  505.                             console.error('Erreur: ', data.message);
  506.                         }
  507.                     })
  508.                     .catch(error => console.error('Erreur:', error));
  509.             });
  510.         }
  511.         function updateTable(recette, totals) {
  512.             const table = document.getElementById('recettesTable').getElementsByTagName('tbody')[0];
  513.             const newRow = table.insertRow(table.rows.length);
  514.             newRow.setAttribute('data-id', recette.id);
  515.             newRow.innerHTML = `
  516.         <td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">${recette.nomClient}</td>
  517.         <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
  518.             <ul>
  519.                 ${recette.prestations.map(prestation => `<li>${prestation.nom}</li>`).join('')}
  520.             </ul>
  521.         </td>
  522.         <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">${recette.acompte ? recette.acompte + ' â‚¬' : '-'}</td>
  523.         <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
  524.             <ul>
  525.                 ${recette.paiements.map(paiement => `<li>${paiement.sommePayee ? paiement.sommePayee + ' â‚¬' : '-'}</li>`).join('')}
  526.             </ul>
  527.         </td>
  528.         <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
  529.             <ul>
  530.                 ${recette.paiements.map(paiement => `<li>${paiement.typePaiement.nom}</li>`).join('')}
  531.             </ul>
  532.         </td>
  533.         <td>
  534.             <button
  535.                 class="relative h-10 max-h-[40px] w-10 max-w-[40px] select-none
  536.                 rounded-lg bg-gray-900 text-center align-middle font-sans text-xs font-medium
  537.                 uppercase text-white shadow-md shadow-gray-900/10 transition-all hover:shadow-lg
  538.                 hover:shadow-gray-900/20 focus:opacity-[0.85] focus:shadow-none
  539.                 active:opacity-[0.85] active:shadow-none disabled:pointer-events-none
  540.                 disabled:opacity-50 disabled:shadow-none"
  541.                 type="button"
  542.                 data-ripple-light="true"
  543.                 data-modal-target="deleteModal"
  544.                 data-modal-toggle="deleteModal"
  545.                 onclick="showDeleteModal(${recette.id})"
  546.             >
  547.                 <span class="absolute transform -translate-x-1/2 -translate-y-1/2 top-1/2 left-1/2">
  548.                     <i class="fas fa-trash" aria-hidden="true"></i>
  549.                 </span>
  550.             </button>
  551.         </td>`;
  552.             document.getElementById('totalJour').textContent = `Total : ${totals.totalJour} â‚¬`;
  553.             document.getElementById('totalEspece').textContent = `${totals.totalEspece} â‚¬`;
  554.             document.getElementById('totalCarteBancaire').textContent = `${totals.totalCarteBancaire} â‚¬`;
  555.             document.getElementById('totalAcompte').textContent = `${totals.totalAcompte} â‚¬`;
  556.             document.getElementById('recettesContainer').style.display = 'block';
  557.         }
  558.         function showDiscountInput() {
  559.             var discountInputContainer = document.getElementById('discountInputContainer');
  560.             discountInputContainer.style.display = 'block';
  561.         }
  562.         function initDatePicker() {
  563.             const urlDate = new URLSearchParams(window.location.search).get('date');
  564.             const parsedDate = urlDate ? flatpickr.parseDate(urlDate, "d/m/Y") : new Date();
  565.             const fp = flatpickr("#dateDisplay", {
  566.                 enableTime: false,
  567.                 dateFormat: "d/m/Y",
  568.                 defaultDate: parsedDate,
  569.                 locale: {
  570.                     firstDayOfWeek: 1,
  571.                     weekdays: {
  572.                         shorthand: ['Dim', 'Lun', 'Mar', 'Mer', 'Jeu', 'Ven', 'Sam'],
  573.                         longhand: ['Dimanche', 'Lundi', 'Mardi', 'Mercredi', 'Jeudi', 'Vendredi', 'Samedi'],
  574.                     },
  575.                     months: {
  576.                         shorthand: ['Jan', 'FĂ©v', 'Mar', 'Avr', 'Mai', 'Juin', 'Juil', 'AoĂ»', 'Sep', 'Oct', 'Nov', 'DĂ©c'],
  577.                         longhand: ['Janvier', 'FĂ©vrier', 'Mars', 'Avril', 'Mai', 'Juin', 'Juillet', 'AoĂ»t', 'Septembre', 'Octobre', 'Novembre', 'DĂ©cembre'],
  578.                     },
  579.                 },
  580.                 onClose: function(selectedDates, dateStr, instance) {
  581.                     window.location.href = `${window.location.pathname}?date=${dateStr}`;
  582.                 },
  583.                 disableMobile: true // DĂ©sactive l'interface mobile de Flatpickr
  584.             });
  585.             const months = ['Janvier', 'FĂ©vrier', 'Mars', 'Avril', 'Mai', 'Juin', 'Juillet', 'AoĂ»t', 'Septembre', 'Octobre', 'Novembre', 'DĂ©cembre'];
  586.             const day = parsedDate.getDate();
  587.             const month = months[parsedDate.getMonth()];
  588.             const year = parsedDate.getFullYear();
  589.             document.getElementById('dateDisplay').innerText = `${day} ${month} ${year}`;
  590.         }
  591.         function openCalendar() {
  592.             document.querySelector('#dateDisplay').click(); // Correction : Utilisez l'ID unique pour ouvrir le calendrier
  593.         }
  594.         function initClientAutocomplete() {
  595.             const nomClientInput = document.getElementById('nomClient');
  596.             let timeoutId;
  597.             nomClientInput.addEventListener('input', function(e) {
  598.                 clearTimeout(timeoutId);
  599.                 const searchValue = e.target.value;
  600.                 timeoutId = setTimeout(() => {
  601.                     if (searchValue.length >= 1) {
  602.                         fetch(`/admin/search-clients-planity?q=${encodeURIComponent(searchValue)}`)
  603.                             .then(response => response.json())
  604.                             .then(data => {
  605.                                 showSuggestions(data.hits || []);
  606.                             })
  607.                             .catch(error => console.error('Erreur:', error));
  608.                     } else {
  609.                         hideSuggestions();
  610.                     }
  611.                 }, 300);
  612.             });
  613.             // CrĂ©er et ajouter le conteneur de suggestions
  614.             const suggestionsContainer = document.createElement('div');
  615.             suggestionsContainer.id = 'clientSuggestions';
  616.             suggestionsContainer.className = 'absolute z-10 w-full bg-white border border-gray-300 rounded-md shadow-lg hidden';
  617.             nomClientInput.parentNode.style.position = 'relative';
  618.             nomClientInput.parentNode.appendChild(suggestionsContainer);
  619.             function showSuggestions(suggestions) {
  620.                 const container = document.getElementById('clientSuggestions');
  621.                 container.innerHTML = '';
  622.                 if (suggestions.length > 0) {
  623.                     suggestions.forEach(suggestion => {
  624.                         const div = document.createElement('div');
  625.                         div.className = 'p-2 hover:bg-gray-100 cursor-pointer';
  626.                         div.textContent = suggestion.name;
  627.                         div.onclick = () => {
  628.                             nomClientInput.value = suggestion.name;
  629.                             hideSuggestions();
  630.                         };
  631.                         container.appendChild(div);
  632.                     });
  633.                     container.classList.remove('hidden');
  634.                 } else {
  635.                     hideSuggestions();
  636.                 }
  637.             }
  638.             function hideSuggestions() {
  639.                 const container = document.getElementById('clientSuggestions');
  640.                 container.classList.add('hidden');
  641.             }
  642.             // Cacher les suggestions quand on clique ailleurs
  643.             document.addEventListener('click', function(e) {
  644.                 if (!nomClientInput.contains(e.target) && !suggestionsContainer.contains(e.target)) {
  645.                     hideSuggestions();
  646.                 }
  647.             });
  648.         }
  649.         // Ajouter l'appel Ă  initClientAutocomplete dans le DOMContentLoaded
  650.         document.addEventListener('DOMContentLoaded', function() {
  651.             // ... autres initialisations ...
  652.             initClientAutocomplete();
  653.         });
  654.     </script>
  655. {% endblock %}