templates/stock/index.html.twig line 1

Open in your IDE?
  1. {% extends 'backoffice.base.html.twig' %}
  2. {% block title %}Gestion du Stock{% endblock %}
  3. {% block stylesheets %}
  4. {{ parent() }}
  5. <style>
  6.     /* Styles personnalisés */
  7.     .card {
  8.         border-radius: 12px;
  9.         box-shadow: 0 4px 20px rgba(0,0,0,0.05);
  10.         border: none;
  11.     }
  12.     
  13.     .card-header {
  14.         background-color: #f8f9fa;
  15.         border-bottom: 1px solid #eee;
  16.         padding: 1.5rem 1.5rem;
  17.     }
  18.     
  19.     .table {
  20.         font-size: 0.95rem;
  21.     }
  22.     
  23.     .table thead th {
  24.         border-bottom-width: 1px;
  25.         font-weight: 600;
  26.         color: #6c757d;
  27.         text-transform: uppercase;
  28.         letter-spacing: 0.5px;
  29.         font-size: 0.75rem;
  30.     }
  31.     
  32.     .table > :not(:last-child) > :last-child > * {
  33.         border-bottom-color: #f1f1f1;
  34.     }
  35.     
  36.     .btn-outline-primary {
  37.         border-width: 2px;
  38.         font-weight: 500;
  39.     }
  40.     
  41.     .btn-sm {
  42.         padding: 0.35rem 0.75rem;
  43.         font-size: 0.85rem;
  44.     }
  45.     
  46.     .menu-sub {
  47.         box-shadow: 0 5px 25px rgba(0,0,0,0.1);
  48.         border: none;
  49.         border-radius: 8px;
  50.     }
  51.     
  52.     .menu-link {
  53.         transition: all 0.2s;
  54.         font-weight: 500;
  55.     }
  56.     
  57.     .menu-link:hover {
  58.         background-color: #f3f6f9;
  59.         color: #009ef7;
  60.     }
  61.     
  62.     tr {
  63.         transition: all 0.2s;
  64.     }
  65.     
  66.     tr:hover {
  67.         background-color: #fbfbfb;
  68.     }
  69.     
  70.     .quantity-badge {
  71.         display: inline-block;
  72.         padding: 0.25rem 0.5rem;
  73.         border-radius: 50px;
  74.         font-weight: 600;
  75.         font-size: 0.8rem;
  76.         min-width: 30px;
  77.         text-align: center;
  78.     }
  79.     
  80.     .export-btn {
  81.         border-radius: 8px;
  82.         font-weight: 600;
  83.         padding: 0.5rem 1.25rem;
  84.     }
  85.     
  86.     /* Modal styles */
  87.     #deleteStockModal .modal-header {
  88.         background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);
  89.         border-bottom: 2px solid #dee2e6;
  90.     }
  91.     
  92.     #deleteStockModal .modal-title {
  93.         font-weight: 600;
  94.         color: #dc3545;
  95.     }
  96.     
  97.     #deleteStockModal .btn-danger {
  98.         background: linear-gradient(135deg, #dc3545 0%, #c82333 100%);
  99.         border: none;
  100.         transition: all 0.3s ease;
  101.     }
  102.     
  103.     #deleteStockModal .btn-danger:hover {
  104.         transform: translateY(-2px);
  105.         box-shadow: 0 4px 8px rgba(220, 53, 69, 0.3);
  106.     }
  107.     
  108.     #deleteStockModal .input-group-text {
  109.         background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);
  110.         border-right: none;
  111.     }
  112.     
  113.     #deleteStockModal .form-control:focus {
  114.         border-color: #dc3545;
  115.         box-shadow: 0 0 0 0.2rem rgba(220, 53, 69, 0.25);
  116.     }
  117.     
  118.     .menu-link.text-muted {
  119.         opacity: 0.6;
  120.         cursor: not-allowed;
  121.         pointer-events: none;
  122.         background-color: #f8f9fa;
  123.     }
  124.     
  125.     .menu-link.text-muted:hover {
  126.         background-color: #f8f9fa !important;
  127.         color: #6c757d !important;
  128.     }
  129.     
  130.     @keyframes shake {
  131.         0%, 100% { transform: translateX(0); }
  132.         10%, 30%, 50%, 70%, 90% { transform: translateX(-5px); }
  133.         20%, 40%, 60%, 80% { transform: translateX(5px); }
  134.     }
  135.     
  136.     .shake-animation {
  137.         animation: shake 0.5s ease-in-out;
  138.     }
  139. </style>
  140. {% endblock %}
  141. {% block body %}
  142. {{ include('Base/_sidebar_admin.html.twig') }}
  143. <div class="post d-flex flex-column-fluid" id="kt_post">
  144.     <div id="kt_content_container" class="container-xxl">
  145.         <div class="card shadow-sm">
  146.             <div class="card-header border-0 pt-6">
  147.                 <div class="card-title">
  148.                     <h2 class="fw-bolder mb-0">Gestion du Stock</h2>
  149.                 </div>
  150.                 <div class="card-toolbar">
  151.                     <div class="d-flex justify-content-end" data-kt-customer-table-toolbar="base">
  152.                         <a class="btn btn-light-primary btn-hover-rise me-2" href="{{ path('inventaire_index') }}">
  153.                             <i class="fas fa-clipboard-list me-2"></i>Rapports d'inventaire
  154.                         </a>
  155.                         <a class="btn btn-light-primary btn-hover-rise export-btn" href="{{ path('export_donnees_csv_stock') }}">
  156.                             <i class="fas fa-file-export me-2"></i>Exporter en CSV
  157.                         </a>
  158.                     </div>
  159.                 </div>
  160.             </div>
  161.             <div class="card-body pt-0">
  162.                 <div class="mb-5">
  163.                     <div class="position-relative">
  164.                         <i class="fas fa-search position-absolute ms-4 fs-4" style="top:50%;transform:translateY(-50%);color:#a1a5b7;"></i>
  165.                         <input type="text" id="stockSearchInput" class="form-control form-control-solid ps-13"
  166.                                style="height:48px;font-size:1rem;"
  167.                                placeholder="Rechercher par code-barre, nom de pièce ou référence...">
  168.                     </div>
  169.                     <div id="stockSearchEmpty" class="text-center py-10 d-none">
  170.                         <i class="fas fa-search fs-4x text-muted mb-4"></i>
  171.                         <div class="text-muted fw-bold fs-5">Aucun résultat pour cette recherche</div>
  172.                     </div>
  173.                 </div>
  174.                 <!-- Message flash -->
  175.                 {% for label, messages in app.flashes %}
  176.                     {% for message in messages %}
  177.                         <div class="alert alert-dismissible bg-light-{{ label == 'error' ? 'danger' : label == 'success' ? 'success' : 'info' }} d-flex flex-column flex-sm-row p-5 mb-10">
  178.                             <div class="d-flex flex-column pe-0 pe-sm-10">
  179.                                 <span>{{ message }}</span>
  180.                             </div>
  181.                             <button type="button" class="position-absolute position-sm-relative m-2 m-sm-0 top-0 end-0 btn btn-icon ms-sm-auto" data-bs-dismiss="alert">
  182.                                 <span class="svg-icon svg-icon-1">
  183.                                     <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none">
  184.                                         <rect opacity="0.5" x="6" y="17.3137" width="16" height="2" rx="1" transform="rotate(-45 6 17.3137)" fill="currentColor"/>
  185.                                         <rect x="7.41422" y="6" width="16" height="2" rx="1" transform="rotate(45 7.41422 6)" fill="currentColor"/>
  186.                                     </svg>
  187.                                 </span>
  188.                             </button>
  189.                         </div>
  190.                     {% endfor %}
  191.                 {% endfor %}
  192.                 
  193.                 <div class="table-responsive">
  194.                     <table class="table align-middle table-row-dashed fs-6 gy-5" id="kt_customers_table">
  195.                         <thead>
  196.                             <tr class="text-start text-gray-600 fw-bolder fs-7 text-uppercase gs-0">
  197.                                 <th class="min-w-25px">
  198.                                     <div class="form-check form-check-sm form-check-custom form-check-solid">
  199.                                         <input class="form-check-input" type="checkbox" value="1" data-kt-check="true" data-kt-check-target=".widget-9-check"/>
  200.                                     </div>
  201.                                 </th>
  202.                                 <th class="min-w-100px">Pièce</th>
  203.                                 <th class="text-center min-w-80px">Quantité</th>
  204.                                 <th class="min-w-100px">Montant HTVA</th>
  205.                                 <th class="min-w-100px">Num. BL</th>
  206.                                 <th class="min-w-120px">Référence</th>
  207.                                 <th class="min-w-150px">Code-barres</th>
  208.                                 <th class="text-end min-w-100px">Actions</th>
  209.                             </tr>
  210.                         </thead>
  211.                         <tbody class="fw-semibold text-gray-700">
  212.                             {% for stock in stocks %}
  213.                             {% set nomPiece = stock.piece ?: (stock.idProduit ? stock.idProduit.description : null) %}
  214.                             <tr class="stock-row" data-search="{{ (nomPiece ~ ' ' ~ stock.reference ~ ' ' ~ stock.codeBarre)|lower }}">
  215.                                 <td>
  216.                                     <div class="form-check form-check-sm form-check-custom form-check-solid">
  217.                                         <input class="form-check-input widget-9-check" type="checkbox" value="1"/>
  218.                                     </div>
  219.                                 </td>
  220.                                 <td>
  221.                                     <div class="d-flex align-items-center">
  222.                                         <div class="symbol symbol-40px me-3">
  223.                                             <span class="symbol-label bg-light-primary">
  224.                                                 <i class="fas fa-box-open text-primary"></i>
  225.                                             </span>
  226.                                         </div>
  227.                                         <div>
  228.                                             <span class="text-dark fw-bold d-block">
  229.                                                 {{ stock.piece ?: (stock.idProduit ? stock.idProduit.description : null) ?: (stock.reference ?: 'N/A') }}
  230.                                             </span>
  231.                                         </div>
  232.                                     </div>
  233.                                 </td>
  234.                                 <td class="text-center">
  235.                                     {% set stock_class = 'success' %}
  236.                                     {% if stock.quantite <= 5 %}
  237.                                         {% set stock_class = 'danger' %}
  238.                                     {% elseif stock.quantite <= 10 %}
  239.                                         {% set stock_class = 'warning' %}
  240.                                     {% endif %}
  241.                                     <span class="quantity-badge bg-light-{{ stock_class }} text-{{ stock_class }}">
  242.                                         {{ stock.quantite }}
  243.                                     </span>
  244.                                 </td>
  245.                                 <td>{{ stock.montantHTVA|number_format(2, ',', ' ') }} TND</td>
  246.                                 <td>
  247.                                     <span class="badge badge-light-primary">{{ stock.numBl ?: 'N/A' }}</span>
  248.                                 </td>
  249.                                 <td>
  250.                                     <span class="text-dark fw-bold">{{ stock.reference ?: 'N/A' }}</span>
  251.                                 </td>
  252.                                 <td>
  253.                                     <span class="badge badge-light-dark">{{ stock.codeBarre ?: 'N/A' }}</span>
  254.                                 </td>
  255.                                 <td class="text-end">
  256.                                     <div class="dropdown">
  257.                                         <button class="btn btn-sm btn-icon btn-color-gray-400 btn-active-light-primary" data-bs-toggle="dropdown">
  258.                                             <span class="svg-icon svg-icon-2">
  259.                                                 <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none">
  260.                                                     <rect x="10" y="10" width="4" height="4" rx="2" fill="currentColor"/>
  261.                                                     <rect x="10" y="3" width="4" height="4" rx="2" fill="currentColor"/>
  262.                                                     <rect x="10" y="17" width="4" height="4" rx="2" fill="currentColor"/>
  263.                                                 </svg>
  264.                                             </span>
  265.                                         </button>
  266.                                         <div class="dropdown-menu menu menu-sub menu-sub-dropdown menu-column menu-rounded menu-gray-600 menu-state-bg-light-primary fw-bold fs-7 w-150px py-4" data-kt-menu="true">
  267.                                             <div class="menu-item px-3">
  268.                                                 <a href="{{ path('app_stock_show', {'id': stock.id}) }}" class="menu-link px-3">
  269.                                                     <span class="svg-icon svg-icon-3 me-2">
  270.                                                         <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none">
  271.                                                             <path d="M21.7 18.9L18.6 15.8C17.9 16.9 16.9 17.9 15.8 18.6L18.9 21.7C19.3 22.1 19.9 22.1 20.3 21.7L21.7 20.3C22.1 19.9 22.1 19.3 21.7 18.9Z" fill="currentColor"/>
  272.                                                             <path opacity="0.3" d="M11 20C6 20 2 16 2 11C2 6 6 2 11 2C16 2 20 6 20 11C20 16 16 20 11 20ZM11 4C7.1 4 4 7.1 4 11C4 14.9 7.1 18 11 18C14.9 18 18 14.9 18 11C18 7.1 14.9 4 11 4ZM8 11C8 9.3 9.3 8 11 8C11.6 8 12 7.6 12 7C12 6.4 11.6 6 11 6C8.2 6 6 8.2 6 11C6 11.6 6.4 12 7 12C7.6 12 8 11.6 8 11Z" fill="currentColor"/>
  273.                                                         </svg>
  274.                                                     </span>
  275.                                                     Afficher
  276.                                                 </a>
  277.                                             </div>
  278.                                             <div class="menu-item px-3">
  279.                                                 <a href="{{ path('app_stock_edit', {'id': stock.id}) }}" class="menu-link px-3">
  280.                                                     <span class="svg-icon svg-icon-3 me-2">
  281.                                                         <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none">
  282.                                                             <path opacity="0.3" d="M21.4 8.35303L19.241 10.511L13.485 4.755L15.643 2.59595C16.0248 2.21423 16.5426 1.99988 17.0825 1.99988C17.6224 1.99988 18.1402 2.21423 18.522 2.59595L21.4 5.474C21.7817 5.85581 21.9962 6.37355 21.9962 6.91345C21.9962 7.45335 21.7817 7.97122 21.4 8.35303ZM3.68699 21.932L9.88699 19.865L4.13099 14.109L2.06399 20.309C1.98815 20.5354 1.97703 20.7787 2.03189 21.0111C2.08674 21.2436 2.2054 21.4561 2.37449 21.6248C2.54359 21.7934 2.75641 21.9115 2.989 21.9658C3.22158 22.0201 3.4647 22.0084 3.69099 21.932H3.68699Z" fill="currentColor"/>
  283.                                                             <path d="M5.574 21.3L3.692 21.928C3.46591 22.0032 3.22334 22.0141 2.99144 21.9594C2.75954 21.9046 2.54744 21.7864 2.3789 21.6179C2.21036 21.4495 2.09202 21.2375 2.03711 21.0056C1.9822 20.7737 1.99289 20.5312 2.06799 20.3051L2.696 18.422L5.574 21.3ZM4.13499 14.105L9.891 19.861L19.245 10.507L13.489 4.75098L4.13499 14.105Z" fill="currentColor"/>
  284.                                                         </svg>
  285.                                                     </span>
  286.                                                     Modifier
  287.                                                 </a>
  288.                                             </div>
  289.                                             <div class="menu-item px-3">
  290.                                                 <a href="javascript:void(0)" 
  291.                                                    class="menu-link px-3 text-danger" 
  292.                                                    onclick="if (typeof checkAndOpenDeleteStockModal === 'function') checkAndOpenDeleteStockModal({{ stock.id }}, '{{ (stock.reference ?? stock.piece ?? 'Stock')|replace({"'": "\\'", '"': '&quot;'}) }}'); else alert('Fonction de suppression non chargée');">
  293.                                                     <span class="svg-icon svg-icon-3 me-2">
  294.                                                         <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none">
  295.                                                             <path d="M5 9C5 8.44772 5.44772 8 6 8H18C18.5523 8 19 8.44772 19 9V18C19 19.6569 17.6569 21 16 21H8C6.34315 21 5 19.6569 5 18V9Z" fill="currentColor"/>
  296.                                                             <path opacity="0.5" d="M5 5C5 4.44772 5.44772 4 6 4H18C18.5523 4 19 4.44772 19 5C19 5.55228 18.5523 6 18 6H6C5.44772 6 5 5.55228 5 5Z" fill="currentColor"/>
  297.                                                             <path opacity="0.5" d="M9 4C9 3.44772 9.44772 3 10 3H14C14.5523 3 15 3.44772 15 4C15 4.55228 14.5523 5 14 5H10C9.44772 5 9 4.55228 9 4Z" fill="currentColor"/>
  298.                                                         </svg>
  299.                                                     </span>
  300.                                                     Supprimer
  301.                                                 </a>
  302.                                             </div>
  303.                                         </div>
  304.                                     </div>
  305.                                 </td>
  306.                             </tr>
  307.                             {% else %}
  308.                             <tr>
  309.                                 <td colspan="8" class="text-center py-10">
  310.                                     <div class="d-flex flex-column align-items-center">
  311.                                         <i class="fas fa-box-open fs-4x text-muted mb-4"></i>
  312.                                         <span class="text-muted fw-bold fs-5">Aucun article en stock</span>
  313.                                     </div>
  314.                                 </td>
  315.                             </tr>
  316.                             {% endfor %}
  317.                         </tbody>
  318.                     </table>
  319.                 </div>
  320.             </div>
  321.         </div>
  322.         <!-- MODAL DE SUPPRESSION STOCK -->
  323.         <div class="modal fade" id="deleteStockModal" tabindex="-1" aria-labelledby="deleteStockModalLabel" aria-hidden="true">
  324.             <div class="modal-dialog">
  325.                 <div class="modal-content">
  326.                     <div class="modal-header">
  327.                         <h5 class="modal-title" id="deleteStockModalLabel">
  328.                             <i class="bi bi-exclamation-triangle-fill text-warning me-2"></i>
  329.                             Confirmer la suppression
  330.                         </h5>
  331.                         <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
  332.                     </div>
  333.                     <div class="modal-body">
  334.                         <div id="deleteStockMessage"></div>
  335.                         <form id="deleteStockForm">
  336.                             <input type="hidden" name="stock_id" id="stockIdInput" value="">
  337.                             <div class="mb-3">
  338.                                 <label for="deleteStockCode" class="form-label">
  339.                                     <strong>Code de confirmation</strong>
  340.                                 </label>
  341.                                 <div class="input-group">
  342.                                     <span class="input-group-text bg-light">
  343.                                         <i class="bi bi-shield-lock"></i>
  344.                                     </span>
  345.                                     <input type="password" class="form-control" id="deleteStockCode" 
  346.                                            name="delete_code" placeholder="Saisissez 123123" required maxlength="6">
  347.                                 </div>
  348.                                 <div class="form-text text-muted">
  349.                                     <i class="bi bi-info-circle"></i> Entrez le code de sécurité 123123 pour confirmer
  350.                                 </div>
  351.                             </div>
  352.                             <input type="hidden" name="_token" value="{{ csrf_token('delete_stock') }}">
  353.                         </form>
  354.                     </div>
  355.                     <div class="modal-footer">
  356.                         <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">
  357.                             <i class="bi bi-x-circle me-1"></i>
  358.                             Annuler
  359.                         </button>
  360.                         <button type="button" class="btn btn-danger" id="confirmDeleteStockBtn">
  361.                             <span class="spinner-border spinner-border-sm d-none me-2" role="status" aria-hidden="true"></span>
  362.                             <i class="bi bi-trash me-1"></i>
  363.                             Supprimer le stock
  364.                         </button>
  365.                     </div>
  366.                 </div>
  367.             </div>
  368.         </div>
  369.     </div>
  370. </div>
  371. <div class="modal fade" id="kt_modal_add_customer_ok" tabindex="-1" aria-hidden="true">
  372.     <div class="modal-dialog modal-dialog-centered mw-650px">
  373.         <div class="modal-content">
  374.             <form class="form" method="post" action="{{ path('app_stock_new') }}"
  375.                   id="kt_modal_add_customer_form" data-kt-redirect="apps/customers/list.html">
  376.                 <div class="post d-flex flex-column-fluid" id="kt_post">
  377.                     <div id="kt_content_container" class="container">
  378.                         <div class="card">
  379.                             <div class="card-header border-0 pt-6"></div>
  380.                             <div class="card-body pt-0">
  381.                                 {{ render(path('app_stock_new')) }}
  382.                             </div>
  383.                         </div>
  384.                     </div>
  385.                 </div>
  386.             </form>
  387.         </div>
  388.     </div>
  389. </div>
  390. {% endblock %}
  391. {% block javascripts %}
  392. {{ parent() }}
  393. <script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
  394. <script>
  395. (function() {
  396.     'use strict';
  397.     
  398.     let currentStockId = null;
  399.     // ✅ FONCTION GLOBALE DISPONIBLE IMMÉDIATEMENT
  400.     window.checkAndOpenDeleteStockModal = async function(stockId, stockName) {
  401.         try {
  402.             currentStockId = stockId;
  403.             
  404.             // Appel AJAX pour vérifier si le stock peut être supprimé
  405.             const response = await fetch(`/stock/check-delete/${stockId}`);
  406.             
  407.             if (!response.ok) {
  408.                 throw new Error(`HTTP error! status: ${response.status}`);
  409.             }
  410.             
  411.             const data = await response.json();
  412.             if (data.success && data.canDelete) {
  413.                 // Peut supprimer : ouvrir modal
  414.                 const stockIdInput = document.getElementById('stockIdInput');
  415.                 const deleteMessage = document.getElementById('deleteStockMessage');
  416.                 const deleteCode = document.getElementById('deleteStockCode');
  417.                 
  418.                 if (stockIdInput) stockIdInput.value = stockId;
  419.                 
  420.                 if (deleteMessage) {
  421.                     deleteMessage.innerHTML = 
  422.                         `<div class="alert alert-warning d-flex align-items-center mb-3">
  423.                             <i class="bi bi-exclamation-triangle-fill fs-3 me-3"></i>
  424.                             <div>
  425.                                 <strong>Attention !</strong>
  426.                                 <p class="mb-0">Voulez-vous vraiment supprimer cette ligne de stock <strong>"${stockName}"</strong> ?</p>
  427.                             </div>
  428.                         </div>
  429.                         <div class="alert alert-success d-flex align-items-center">
  430.                             <i class="bi bi-check-circle-fill me-2"></i>
  431.                             <span>Cette ligne de stock peut être supprimée</span>
  432.                         </div>`;
  433.                 }
  434.                 
  435.                 if (deleteCode) {
  436.                     deleteCode.value = '';
  437.                     setTimeout(() => deleteCode.focus(), 500);
  438.                 }
  439.                 
  440.                 // Ouvrir modal
  441.                 const modalElement = document.getElementById('deleteStockModal');
  442.                 if (modalElement) {
  443.                     const modal = new bootstrap.Modal(modalElement);
  444.                     modal.show();
  445.                 }
  446.             } else {
  447.                 // Ne peut pas supprimer
  448.                 showStockAlert('error', 'Suppression impossible', data.message);
  449.             }
  450.         } catch (error) {
  451.             console.error('Erreur détaillée:', error);
  452.             showStockAlert('error', 'Erreur', 'Erreur lors de la vérification: ' + error.message);
  453.         }
  454.     };
  455.     // Fonction d'alerte unifiée avec SweetAlert2
  456.     function showStockAlert(icon, title, text, reload = false) {
  457.         if (typeof Swal !== 'undefined') {
  458.             Swal.fire({
  459.                 icon: icon,
  460.                 title: title,
  461.                 text: text,
  462.                 timer: reload ? 2000 : undefined,
  463.                 showConfirmButton: !reload,
  464.                 showCancelButton: false,
  465.                 confirmButtonColor: icon === 'success' ? '#28a745' : '#dc3545',
  466.                 confirmButtonText: icon === 'success' ? 'OK' : 'Fermer'
  467.             }).then((result) => {
  468.                 if (reload) {
  469.                     window.location.reload();
  470.                 }
  471.             });
  472.         } else {
  473.             alert(`${icon === 'success' ? '✅' : '❌'} ${title}\n${text}`);
  474.             if (reload) {
  475.                 window.location.reload();
  476.             }
  477.         }
  478.     }
  479.     // Initialisation après chargement DOM
  480.     document.addEventListener('DOMContentLoaded', function() {
  481.         console.log('✅ Script suppression stock chargé');
  482.         // Recherche instantanée : code-barre, nom de pièce ou référence
  483.         const searchInput = document.getElementById('stockSearchInput');
  484.         const searchEmpty = document.getElementById('stockSearchEmpty');
  485.         const stockRows = document.querySelectorAll('.stock-row');
  486.         if (searchInput) {
  487.             searchInput.addEventListener('input', function () {
  488.                 const term = this.value.trim().toLowerCase();
  489.                 let visibleCount = 0;
  490.                 stockRows.forEach(function (row) {
  491.                     const match = !term || (row.dataset.search || '').includes(term);
  492.                     row.classList.toggle('d-none', !match);
  493.                     if (match) visibleCount++;
  494.                 });
  495.                 if (searchEmpty) {
  496.                     searchEmpty.classList.toggle('d-none', visibleCount !== 0 || stockRows.length === 0);
  497.                 }
  498.             });
  499.         }
  500.         // Bouton confirmer suppression stock
  501.         const confirmBtn = document.getElementById('confirmDeleteStockBtn');
  502.         if (confirmBtn) {
  503.             confirmBtn.addEventListener('click', async function(e) {
  504.                 e.preventDefault();
  505.                 
  506.                 const codeInput = document.getElementById('deleteStockCode');
  507.                 const code = codeInput ? codeInput.value.trim() : '';
  508.                 const stockId = currentStockId;
  509.                 
  510.                 if (!stockId) {
  511.                     showStockAlert('error', 'Erreur', 'ID stock manquant');
  512.                     return;
  513.                 }
  514.                 
  515.                 if (code !== '123123') {
  516.                     showStockAlert('error', 'Code incorrect', 'Le code requis est : 123123');
  517.                     
  518.                     // Animation shake
  519.                     if (codeInput) {
  520.                         codeInput.classList.add('shake-animation');
  521.                         setTimeout(() => codeInput.classList.remove('shake-animation'), 500);
  522.                         codeInput.value = '';
  523.                         codeInput.focus();
  524.                     }
  525.                     return;
  526.                 }
  527.                 // Désactiver bouton + spinner
  528.                 const btn = this;
  529.                 const spinner = btn.querySelector('.spinner-border');
  530.                 const btnHtml = btn.innerHTML;
  531.                 
  532.                 btn.disabled = true;
  533.                 if (spinner) spinner.classList.remove('d-none');
  534.                 btn.innerHTML = '<span class="spinner-border spinner-border-sm me-2" role="status" aria-hidden="true"></span>Suppression en cours...';
  535.                 try {
  536.                     const tokenInput = document.querySelector('#deleteStockForm input[name="_token"]');
  537.                     const token = tokenInput ? tokenInput.value : '';
  538.                     
  539.                     const formData = new FormData();
  540.                     formData.append('_token', token);
  541.                     
  542.                     const response = await fetch(`/stock/${stockId}/delete`, {
  543.                         method: 'POST',
  544.                         body: formData,
  545.                         headers: {
  546.                             'X-Requested-With': 'XMLHttpRequest'
  547.                         }
  548.                     });
  549.                     if (!response.ok) {
  550.                         throw new Error(`HTTP error! status: ${response.status}`);
  551.                     }
  552.                     
  553.                     const data = await response.json();
  554.                     if (data.success) {
  555.                         // Fermer la modal
  556.                         const modalElement = document.getElementById('deleteStockModal');
  557.                         if (modalElement) {
  558.                             const modal = bootstrap.Modal.getInstance(modalElement);
  559.                             if (modal) modal.hide();
  560.                         }
  561.                         
  562.                         // Afficher succès et recharger
  563.                         showStockAlert('success', 'Supprimé !', data.message, true);
  564.                     } else {
  565.                         showStockAlert('error', 'Erreur', data.message);
  566.                         
  567.                         // Réactiver le bouton
  568.                         btn.innerHTML = btnHtml;
  569.                         btn.disabled = false;
  570.                         if (spinner) spinner.classList.add('d-none');
  571.                         
  572.                         // Reset du code
  573.                         if (codeInput) {
  574.                             codeInput.value = '';
  575.                             codeInput.focus();
  576.                         }
  577.                     }
  578.                 } catch (error) {
  579.                     console.error('Erreur détaillée:', error);
  580.                     showStockAlert('error', 'Erreur', 'Erreur lors de la suppression: ' + error.message);
  581.                     
  582.                     // Réactiver le bouton
  583.                     btn.innerHTML = btnHtml;
  584.                     btn.disabled = false;
  585.                     if (spinner) spinner.classList.add('d-none');
  586.                     
  587.                     // Reset du code
  588.                     if (codeInput) {
  589.                         codeInput.value = '';
  590.                         codeInput.focus();
  591.                     }
  592.                 }
  593.             });
  594.         } else {
  595.             console.error('❌ Bouton confirmDeleteStockBtn non trouvé');
  596.         }
  597.         // Reset modal à la fermeture
  598.         const modalElement = document.getElementById('deleteStockModal');
  599.         if (modalElement) {
  600.             modalElement.addEventListener('hidden.bs.modal', function() {
  601.                 console.log('Modal fermée - nettoyage');
  602.                 const form = document.getElementById('deleteStockForm');
  603.                 const confirmBtn = document.getElementById('confirmDeleteStockBtn');
  604.                 const deleteMessage = document.getElementById('deleteStockMessage');
  605.                 const codeInput = document.getElementById('deleteStockCode');
  606.                 
  607.                 if (form) form.reset();
  608.                 if (confirmBtn) {
  609.                     confirmBtn.disabled = false;
  610.                     confirmBtn.innerHTML = 
  611.                         '<span class="spinner-border spinner-border-sm d-none me-2" role="status" aria-hidden="true"></span><i class="bi bi-trash me-1"></i>Supprimer le stock';
  612.                 }
  613.                 if (deleteMessage) deleteMessage.innerHTML = '';
  614.                 if (codeInput) codeInput.value = '';
  615.                 currentStockId = null;
  616.             });
  617.         }
  618.     });
  619. })();
  620. </script>
  621. {% endblock %}