Browse Source

feat(documents): ajout de filtres avancés pour la recherche de documents

stany.ferer 22 hours ago
parent
commit
bb7bfef774
2 changed files with 522 additions and 29 deletions
  1. 6 3
      core/views/pages/cms.compte.php
  2. 516 26
      core/views/pages/cms.documents.php

+ 6 - 3
core/views/pages/cms.compte.php

@@ -342,9 +342,12 @@ if (!empty($lastRecord)) {
             dateFin = new Date(parseInt(parts[0]), parseInt(parts[1]) - 1, parseInt(parts[2]), 23, 59, 59);
         }
 
+        // Fonction pour normaliser le texte (retirer accents et passer en majuscules)
+        const normalizeText = (text) => (text || '').normalize('NFD').replace(/[\u0300-\u036f]/g, '').toUpperCase();
+
         // Récupérer les mots-clés saisis (séparés par espaces)
         const keywordsInput = document.getElementById('search-label-keywords').value.trim();
-        const keywords = keywordsInput.split(/\s+/).filter(k => k.length > 0).map(k => k.toUpperCase());
+        const keywords = keywordsInput.split(/\s+/).filter(k => k.length > 0).map(k => normalizeText(k));
 
         // Récupérer la recherche de montant et la colonne sélectionnée
         const searchMontant = document.getElementById('searchMontant').value.trim();
@@ -369,8 +372,8 @@ if (!empty($lastRecord)) {
 
             // Filtre par mots-clés dans le label (TOUS les mots-clés doivent être présents)
             if (keywords.length > 0) {
-                const labelUpper = (row.label || '').toUpperCase();
-                const hasAllKeywords = keywords.every(keyword => labelUpper.includes(keyword));
+                const labelNorm = normalizeText(row.label);
+                const hasAllKeywords = keywords.every(keyword => labelNorm.includes(keyword));
                 if (!hasAllKeywords) return false;
             }
 

+ 516 - 26
core/views/pages/cms.documents.php

@@ -25,6 +25,122 @@ echo core::filAriane(array(
     "refresh-json" => "documents"
 ));
 ?>
+
+<!-- Panel de filtres avancés -->
+<div class="card mb-3" id="advancedFiltersPanel">
+    <div class="card-header d-flex justify-content-between align-items-center" style="cursor: pointer;" onclick="toggleAdvancedFilters()">
+        <span><i class="bi bi-funnel"></i> Filtres avancés</span>
+        <i class="bi bi-chevron-up" id="advancedFiltersIcon"></i>
+    </div>
+    <div class="card-body" id="advancedFiltersBody">
+        <div class="row">
+            <!-- Filtre par mots-clés sur le titre -->
+            <div class="col-lg-3 col-md-6 mb-3">
+                <label class="form-label fw-bold">Mots-clés dans le Titre</label>
+                <input type="text" class="form-control form-control-sm" id="search-titre-keywords" placeholder="Ex: facture contrat">
+                <small class="text-muted">Mots-clés séparés par espaces</small>
+            </div>
+
+            <!-- Filtre par plage de dates -->
+            <div class="col-lg-3 col-md-6 mb-3">
+                <label class="form-label fw-bold">Période (Date)</label>
+                <div class="d-flex align-items-center">
+                    <input type="date" id="dateDebut" class="form-control form-control-sm me-2">
+                    <span class="me-2">à</span>
+                    <input type="date" id="dateFin" class="form-control form-control-sm">
+                </div>
+            </div>
+
+            <!-- Filtre par échéance -->
+            <div class="col-lg-3 col-md-6 mb-3">
+                <label class="form-label fw-bold">Période (Échéance)</label>
+                <div class="d-flex align-items-center">
+                    <input type="date" id="deadlineDebut" class="form-control form-control-sm me-2">
+                    <span class="me-2">à</span>
+                    <input type="date" id="deadlineFin" class="form-control form-control-sm">
+                </div>
+            </div>
+
+        </div>
+
+        <div class="row">
+            <!-- Filtre par montant -->
+            <div class="col-lg-3 col-md-6 mb-3">
+                <label class="form-label fw-bold">Recherche montant</label>
+                <input type="text" class="form-control form-control-sm" id="searchMontant" placeholder="Ex: 5086 ou >1000">
+                <small class="text-muted">Recherche exacte ou avec opérateur</small>
+            </div>
+
+            <!-- Filtre par plage de montant -->
+            <div class="col-lg-3 col-md-6 mb-3">
+                <label class="form-label fw-bold">Plage Montant</label>
+                <div class="d-flex align-items-center">
+                    <input type="number" step="0.01" id="minMontant" class="form-control form-control-sm me-2" placeholder="Min" style="width: 100px;">
+                    <span class="me-2">à</span>
+                    <input type="number" step="0.01" id="maxMontant" class="form-control form-control-sm" placeholder="Max" style="width: 100px;">
+                </div>
+            </div>
+
+            <!-- Filtre par tags -->
+            <div class="col-lg-3 col-md-6 mb-3">
+                <label class="form-label fw-bold">Tags</label>
+                <input type="text" class="form-control form-control-sm" id="searchTags" placeholder="Ex: urgent important">
+                <small class="text-muted">Mots-clés séparés par espaces</small>
+            </div>
+        </div>
+
+        <div class="row">
+            <!-- Filtre par Type -->
+            <div class="col-lg-3 col-md-6 mb-3">
+                <label class="form-label fw-bold">Type</label>
+                <select class="form-select form-select-sm" id="filterType">
+                    <option value="">-- Tous les types --</option>
+                </select>
+            </div>
+
+            <!-- Filtre par Attachement -->
+            <div class="col-lg-3 col-md-6 mb-3">
+                <label class="form-label fw-bold">Attachement</label>
+                <select class="form-select form-select-sm" id="filterAttach">
+                    <option value="">-- Tous les attachements --</option>
+                </select>
+            </div>
+
+            <!-- Filtre par Attribution -->
+            <div class="col-lg-3 col-md-6 mb-3">
+                <label class="form-label fw-bold">Attribution</label>
+                <select class="form-select form-select-sm" id="filterAssign">
+                    <option value="">-- Toutes les attributions --</option>
+                </select>
+            </div>
+
+            <!-- Filtre par Statut -->
+            <div class="col-lg-3 col-md-6 mb-3">
+                <label class="form-label fw-bold">Statut</label>
+                <select class="form-select form-select-sm" id="filterStatut">
+                    <option value="">-- Tous les statuts --</option>
+                </select>
+            </div>
+        </div>
+
+        <div class="mt-2">
+            <button type="button" class="btn btn-sm btn-primary me-2" onclick="applyAdvancedFilters()">
+                <i class="bi bi-funnel-fill"></i> Appliquer les filtres
+            </button>
+            <button type="button" class="btn btn-sm btn-outline-secondary" onclick="clearAllAdvancedFilters()">
+                <i class="bi bi-x-circle"></i> Réinitialiser les filtres
+            </button>
+            <span class="ms-3 text-muted" id="filterInfo"></span>
+        </div>
+    </div>
+</div>
+
+<style>
+    #advancedFiltersPanel .card-header:hover {
+        background-color: #f8f9fa;
+    }
+</style>
+
 <div>
     <table
         id="table"
@@ -36,7 +152,6 @@ echo core::filAriane(array(
         data-buttons-align="left"
         data-show-footer="true"
         data-pagination="true"
-        data-filter-control="true"
         data-flat="true"
         data-sort-name="date"
         data-sort-order="desc"
@@ -45,15 +160,15 @@ echo core::filAriane(array(
         data-url="<?php echo $jsonTarget ?>">
         <thead>
             <tr>
-                <th data-sortable="true" data-field="date" data-filter-control="input" data-width="90">Date</th>
-                <th data-sortable="true" data-field="titre" data-filter-control="input">Titre</th>
-                <th data-sortable="true" data-field="label" data-filter-control="select" data-width="125">Type</th>
-                <th data-sortable="true" data-field="attach" data-filter-control="select" data-width="180">Attachement</th>
-                <th data-sortable="true" data-field="deadline" data-filter-control="input" data-width="90">Echéance</th>
-                <th data-sortable="true" data-field="tags" data-filter-control="input" data-width="200">Tags</th>
-                <th data-sortable="true" data-field="assign" data-filter-control="select" data-width="200">Attribution</th>
-                <th data-sortable="true" s data-field="done" data-filter-control="select" data-width="100">Statut</th>
-                <th data-sortable="true" data-field="montant" data-filter-control="input" data-width="110" data-formatter="dataFormatter" data-footer-formatter="montantFormatter">Montant</th>
+                <th data-sortable="true" data-field="date" data-width="90">Date</th>
+                <th data-sortable="true" data-field="titre">Titre</th>
+                <th data-sortable="true" data-field="label" data-width="125">Type</th>
+                <th data-sortable="true" data-field="attach" data-width="180">Attachement</th>
+                <th data-sortable="true" data-field="deadline" data-width="90">Echéance</th>
+                <th data-sortable="true" data-field="tags" data-width="200">Tags</th>
+                <th data-sortable="true" data-field="assign" data-width="200">Attribution</th>
+                <th data-sortable="true" data-field="done" data-width="100">Statut</th>
+                <th data-sortable="true" data-field="montant" data-width="110" data-formatter="dataFormatter" data-footer-formatter="montantFormatter">Montant</th>
                 <th data-field="id" data-formatter="selectFormatter" data-width="60"></th>
             </tr>
         </thead>
@@ -61,29 +176,28 @@ echo core::filAriane(array(
 </div>
 
 <script>
-
-<?php 
+    <?php
     if (core::ifGet("tag")) { ?>
 
-    $(function() {
-        tagValue = "<?php echo core::getGet("tag"); ?>";
+        $(function() {
+            tagValue = "<?php echo core::getGet("tag"); ?>";
 
-        if (tagValue) {
-            function applyTagFilter() {
-                var $input = $('#table input.bootstrap-table-filter-control-tags');
-                console.log('input:', $input.length, 'valeur:', tagValue);
-                if ($input.length) {
-                    $input.val(tagValue).trigger('input');
+            if (tagValue) {
+                function applyTagFilter() {
+                    var $input = $('#table input.bootstrap-table-filter-control-tags');
+                    console.log('input:', $input.length, 'valeur:', tagValue);
+                    if ($input.length) {
+                        $input.val(tagValue).trigger('input');
+                    }
                 }
+                $('#table').on('post-body.bs.table post-header.bs.table', applyTagFilter);
+                setTimeout(applyTagFilter, 800);
             }
-            $('#table').on('post-body.bs.table post-header.bs.table', applyTagFilter);
-            setTimeout(applyTagFilter, 800); 
-        }
-    });
+        });
 
-<?php 
+    <?php
     }
-?>
+    ?>
 
     function selectFormatter(value, row) {
         return '<a href="/document-' + row.id + '.html"><button type="submit" class="btn btn-outline-primary btn-sm">Ouvrir</button></a>';
@@ -105,4 +219,380 @@ echo core::filAriane(array(
         });
         return parseFloat(total) === 0 ? euro.format(0.00) : euro.format(total.toFixed(2));
     }
+
+    // Données originales
+    let originalData = [];
+
+    // Toggle du panneau de filtres avancés
+    function toggleAdvancedFilters() {
+        const body = document.getElementById('advancedFiltersBody');
+        const icon = document.getElementById('advancedFiltersIcon');
+        if (body.style.display === 'none') {
+            body.style.display = 'block';
+            icon.className = 'bi bi-chevron-up';
+        } else {
+            body.style.display = 'none';
+            icon.className = 'bi bi-chevron-down';
+        }
+    }
+
+    // Convertir une date en objet Date (supporte DD/MM/YYYY et YYYY-MM-DD)
+    function parseDate(dateStr) {
+        if (!dateStr) return null;
+
+        // Format DD/MM/YYYY
+        if (dateStr.includes('/')) {
+            const parts = dateStr.split('/');
+            if (parts.length === 3) {
+                return new Date(parseInt(parts[2]), parseInt(parts[1]) - 1, parseInt(parts[0]), 0, 0, 0);
+            }
+        }
+
+        // Format YYYY-MM-DD (format ISO/MySQL)
+        if (dateStr.includes('-')) {
+            const parts = dateStr.split('-');
+            if (parts.length === 3 && parts[0].length === 4) {
+                return new Date(parseInt(parts[0]), parseInt(parts[1]) - 1, parseInt(parts[2]), 0, 0, 0);
+            }
+        }
+
+        return null;
+    }
+
+    // Fonction de filtre pour les montants
+    function filterMontant(searchValue, numValue) {
+        if (!searchValue || searchValue.trim() === '') return true;
+
+        const absNumValue = Math.abs(numValue);
+
+        let searchNormalized = searchValue.trim()
+            .replace(/\s/g, '')
+            .replace(/€/g, '')
+            .replace(/\./g, '')
+            .replace(/,/g, '.');
+
+        let operator = null;
+        if (searchNormalized.startsWith('>=')) {
+            operator = '>=';
+            searchNormalized = searchNormalized.substring(2);
+        } else if (searchNormalized.startsWith('<=')) {
+            operator = '<=';
+            searchNormalized = searchNormalized.substring(2);
+        } else if (searchNormalized.startsWith('>')) {
+            operator = '>';
+            searchNormalized = searchNormalized.substring(1);
+        } else if (searchNormalized.startsWith('<')) {
+            operator = '<';
+            searchNormalized = searchNormalized.substring(1);
+        } else if (searchNormalized.startsWith('=')) {
+            operator = '=';
+            searchNormalized = searchNormalized.substring(1);
+        }
+
+        const searchNum = parseFloat(searchNormalized);
+
+        if (isNaN(searchNum)) {
+            const formattedValue = euro.format(numValue).toLowerCase();
+            return formattedValue.includes(searchValue.toLowerCase());
+        }
+
+        if (operator) {
+            switch (operator) {
+                case '>':
+                    return numValue > searchNum;
+                case '<':
+                    return numValue < searchNum;
+                case '>=':
+                    return numValue >= searchNum;
+                case '<=':
+                    return numValue <= searchNum;
+                case '=':
+                    return Math.abs(numValue - searchNum) < 0.01;
+            }
+        }
+
+        if (Math.abs(numValue - searchNum) < 0.01 || Math.abs(absNumValue - searchNum) < 0.01) {
+            return true;
+        }
+
+        const intPart = Math.floor(absNumValue);
+        const searchInt = Math.floor(searchNum);
+
+        if (intPart === searchInt) {
+            return true;
+        }
+
+        const numStr = absNumValue.toFixed(2);
+        const searchStr = searchInt.toString();
+
+        return numStr.startsWith(searchStr) || intPart.toString().includes(searchStr);
+    }
+
+    // Appliquer tous les filtres avancés
+    function applyAdvancedFilters() {
+        // Récupérer les dates
+        const dateDebutStr = document.getElementById('dateDebut').value;
+        const dateFinStr = document.getElementById('dateFin').value;
+        const deadlineDebutStr = document.getElementById('deadlineDebut').value;
+        const deadlineFinStr = document.getElementById('deadlineFin').value;
+
+        let dateDebut = null,
+            dateFin = null,
+            deadlineDebut = null,
+            deadlineFin = null;
+
+        if (dateDebutStr) {
+            const parts = dateDebutStr.split('-');
+            dateDebut = new Date(parseInt(parts[0]), parseInt(parts[1]) - 1, parseInt(parts[2]), 0, 0, 0);
+        }
+        if (dateFinStr) {
+            const parts = dateFinStr.split('-');
+            dateFin = new Date(parseInt(parts[0]), parseInt(parts[1]) - 1, parseInt(parts[2]), 23, 59, 59);
+        }
+        if (deadlineDebutStr) {
+            const parts = deadlineDebutStr.split('-');
+            deadlineDebut = new Date(parseInt(parts[0]), parseInt(parts[1]) - 1, parseInt(parts[2]), 0, 0, 0);
+        }
+        if (deadlineFinStr) {
+            const parts = deadlineFinStr.split('-');
+            deadlineFin = new Date(parseInt(parts[0]), parseInt(parts[1]) - 1, parseInt(parts[2]), 23, 59, 59);
+        }
+
+        // Fonction pour normaliser le texte (retirer accents et passer en majuscules)
+        const normalizeText = (text) => (text || '').normalize('NFD').replace(/[\u0300-\u036f]/g, '').toUpperCase();
+
+        // Récupérer les mots-clés du titre
+        const titreInput = document.getElementById('search-titre-keywords').value.trim();
+        const titreKeywords = titreInput.split(/\s+/).filter(k => k.length > 0).map(k => normalizeText(k));
+
+        // Récupérer les tags
+        const tagsInput = document.getElementById('searchTags').value.trim();
+        const tagsKeywords = tagsInput.split(/\s+/).filter(k => k.length > 0).map(k => normalizeText(k));
+
+        // Récupérer la recherche de montant
+        const searchMontant = document.getElementById('searchMontant').value.trim();
+
+        const minMontant = parseFloat(document.getElementById('minMontant').value);
+        const maxMontant = parseFloat(document.getElementById('maxMontant').value);
+
+        // Récupérer les filtres Type, Attachement, Attribution, Statut
+        const filterType = document.getElementById('filterType').value;
+        const filterAttach = document.getElementById('filterAttach').value;
+        const filterAssign = document.getElementById('filterAssign').value;
+        const filterStatut = document.getElementById('filterStatut').value;
+
+        let filteredData = originalData.filter(row => {
+            // Filtre par plage de dates
+            if (dateDebut || dateFin) {
+                const rowDate = parseDate(row.date);
+                if (!rowDate) return false;
+                if (dateDebut && rowDate < dateDebut) return false;
+                if (dateFin && rowDate > dateFin) return false;
+            }
+
+            // Filtre par plage d'échéance
+            if (deadlineDebut || deadlineFin) {
+                const rowDeadline = parseDate(row.deadline);
+                if (!rowDeadline) return false;
+                if (deadlineDebut && rowDeadline < deadlineDebut) return false;
+                if (deadlineFin && rowDeadline > deadlineFin) return false;
+            }
+
+            // Filtre par mots-clés dans le titre
+            if (titreKeywords.length > 0) {
+                const titreNorm = normalizeText(row.titre);
+                const hasAllKeywords = titreKeywords.every(keyword => titreNorm.includes(keyword));
+                if (!hasAllKeywords) return false;
+            }
+
+            // Filtre par tags
+            if (tagsKeywords.length > 0) {
+                const tagsNorm = normalizeText(row.tags);
+                const hasAllTags = tagsKeywords.every(keyword => tagsNorm.includes(keyword));
+                if (!hasAllTags) return false;
+            }
+
+            // Filtre par recherche de montant
+            if (searchMontant) {
+                const montant = parseFloat(row.montant) || 0;
+                if (!filterMontant(searchMontant, montant)) return false;
+            }
+
+            // Filtre par plage de montant
+            const montant = parseFloat(row.montant) || 0;
+            if (!isNaN(minMontant) && montant < minMontant) return false;
+            if (!isNaN(maxMontant) && montant > maxMontant) return false;
+
+            // Filtre par Type
+            if (filterType && (row.label || '') !== filterType) return false;
+
+            // Filtre par Attachement
+            if (filterAttach && (row.attach || '') !== filterAttach) return false;
+
+            // Filtre par Attribution
+            if (filterAssign && (row.assign || '') !== filterAssign) return false;
+
+            // Filtre par Statut
+            if (filterStatut && (row.done || '') !== filterStatut) return false;
+
+            return true;
+        });
+
+        // Recharger le tableau avec les données filtrées
+        $('#table').bootstrapTable('load', filteredData);
+        updateFilterInfo(filteredData.length, titreKeywords);
+    }
+
+    // Réinitialiser tous les filtres avancés
+    function clearAllAdvancedFilters() {
+        document.getElementById('dateDebut').value = '';
+        document.getElementById('dateFin').value = '';
+        document.getElementById('deadlineDebut').value = '';
+        document.getElementById('deadlineFin').value = '';
+        document.getElementById('search-titre-keywords').value = '';
+        document.getElementById('searchTags').value = '';
+        document.getElementById('searchMontant').value = '';
+        document.getElementById('minMontant').value = '';
+        document.getElementById('maxMontant').value = '';
+        document.getElementById('filterType').value = '';
+        document.getElementById('filterAttach').value = '';
+        document.getElementById('filterAssign').value = '';
+        document.getElementById('filterStatut').value = '';
+
+        $('#table').bootstrapTable('load', originalData);
+        updateFilterInfo(originalData.length, []);
+    }
+
+    // Mettre à jour l'info des filtres actifs
+    function updateFilterInfo(count, keywords) {
+        const infoEl = document.getElementById('filterInfo');
+        const activeFilters = [];
+
+        const dateDebut = document.getElementById('dateDebut').value;
+        const dateFin = document.getElementById('dateFin').value;
+        if (dateDebut || dateFin) {
+            activeFilters.push(`Date: ${dateDebut || 'début'} à ${dateFin || 'fin'}`);
+        }
+
+        const deadlineDebut = document.getElementById('deadlineDebut').value;
+        const deadlineFin = document.getElementById('deadlineFin').value;
+        if (deadlineDebut || deadlineFin) {
+            activeFilters.push(`Échéance: ${deadlineDebut || 'début'} à ${deadlineFin || 'fin'}`);
+        }
+
+        if (keywords && keywords.length > 0) {
+            activeFilters.push(`Titre: "${keywords.join(' + ')}"`);
+        }
+
+        const searchTags = document.getElementById('searchTags').value;
+        if (searchTags) {
+            activeFilters.push(`Tags: "${searchTags}"`);
+        }
+
+        const searchMontant = document.getElementById('searchMontant').value;
+        if (searchMontant) {
+            activeFilters.push(`Montant: "${searchMontant}"`);
+        }
+
+        const minMontant = document.getElementById('minMontant').value;
+        const maxMontant = document.getElementById('maxMontant').value;
+        if (minMontant || maxMontant) {
+            activeFilters.push(`Plage: ${minMontant || '0'} - ${maxMontant || '∞'}`);
+        }
+
+        const filterType = document.getElementById('filterType').value;
+        if (filterType) {
+            activeFilters.push(`Type: "${filterType}"`);
+        }
+
+        const filterAttach = document.getElementById('filterAttach').value;
+        if (filterAttach) {
+            activeFilters.push(`Attachement: "${filterAttach}"`);
+        }
+
+        const filterAssign = document.getElementById('filterAssign').value;
+        if (filterAssign) {
+            activeFilters.push(`Attribution: "${filterAssign}"`);
+        }
+
+        const filterStatut = document.getElementById('filterStatut').value;
+        if (filterStatut) {
+            activeFilters.push(`Statut: "${filterStatut}"`);
+        }
+
+        if (activeFilters.length > 0) {
+            infoEl.innerHTML = `<strong>${count}</strong> document(s) affiché(s) | Filtres: ${activeFilters.join(', ')}`;
+        } else {
+            infoEl.innerHTML = `<strong>${count || originalData.length}</strong> document(s) au total`;
+        }
+    }
+
+    // Populer les selects avec les valeurs uniques
+    function populateSelectFilters(data) {
+        const types = [...new Set(data.map(row => row.label).filter(v => v))].sort();
+        const attachs = [...new Set(data.map(row => row.attach).filter(v => v))].sort();
+        const assigns = [...new Set(data.map(row => row.assign).filter(v => v))].sort();
+        const statuts = [...new Set(data.map(row => row.done).filter(v => v))].sort();
+
+        const selectType = document.getElementById('filterType');
+        const selectAttach = document.getElementById('filterAttach');
+        const selectAssign = document.getElementById('filterAssign');
+        const selectStatut = document.getElementById('filterStatut');
+
+        // Réinitialiser et repeupler Type
+        selectType.innerHTML = '<option value="">-- Tous les types --</option>';
+        types.forEach(type => {
+            const option = document.createElement('option');
+            option.value = type;
+            option.textContent = type;
+            selectType.appendChild(option);
+        });
+
+        // Réinitialiser et repeupler Attachement
+        selectAttach.innerHTML = '<option value="">-- Tous les attachements --</option>';
+        attachs.forEach(attach => {
+            const option = document.createElement('option');
+            option.value = attach;
+            option.textContent = attach;
+            selectAttach.appendChild(option);
+        });
+
+        // Réinitialiser et repeupler Attribution
+        selectAssign.innerHTML = '<option value="">-- Toutes les attributions --</option>';
+        assigns.forEach(assign => {
+            const option = document.createElement('option');
+            option.value = assign;
+            option.textContent = assign;
+            selectAssign.appendChild(option);
+        });
+
+        // Réinitialiser et repeupler Statut
+        selectStatut.innerHTML = '<option value="">-- Tous les statuts --</option>';
+        statuts.forEach(statut => {
+            const option = document.createElement('option');
+            option.value = statut;
+            option.textContent = statut;
+            selectStatut.appendChild(option);
+        });
+    }
+
+    // Initialisation
+    $(document).ready(function() {
+        const $table = $('#table');
+
+        $table.on('load-success.bs.table', function(e, data) {
+            originalData = data;
+            populateSelectFilters(data);
+            updateFilterInfo(data.length, []);
+        });
+
+        // Appliquer le filtre avec Entrée sur les champs texte
+        ['search-titre-keywords', 'searchTags', 'searchMontant'].forEach(id => {
+            document.getElementById(id).addEventListener('keypress', function(e) {
+                if (e.key === 'Enter') {
+                    applyAdvancedFilters();
+                }
+            });
+        });
+    });
 </script>