cms.documents.php 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598
  1. <?php
  2. $jsonTarget = "/json.php?file=documents";
  3. if (debug::isFile("debug")) {
  4. debug::log(debug::getBadge($jsonTarget, "OUVRIR LE JSON : " . $jsonTarget), "JSON chargé en arrière plan");
  5. }
  6. ?>
  7. <header class="d-flex flex-column flex-md-row align-items-md-center p-3 bg-light ">
  8. <h2 class="bd-title" id="content">
  9. <span>Listes des documents</span>
  10. </h2>
  11. <?php if (access::ifAccesss("add-document")) { ?>
  12. <div class="fix-container-button-nav">
  13. <a href="/add-document.html"><button type="submit" class="btn btn-outline-success btn-sm"><?php icon::getFont(["icon" => "bi bi-file-earmark-plus"]) ?> Ajouter un document</button></a>
  14. </div>
  15. <?php } ?>
  16. </header>
  17. <?php
  18. echo core::filAriane(array(
  19. "current" => "Listes des documents",
  20. "arbo" => array(
  21. "Documents" => NULL,
  22. "Listes des documents" => "/documents.html"
  23. ),
  24. "refresh-json" => "documents"
  25. ));
  26. ?>
  27. <!-- Panel de filtres avancés -->
  28. <div class="card mb-3" id="advancedFiltersPanel">
  29. <div class="card-header d-flex justify-content-between align-items-center" style="cursor: pointer;" onclick="toggleAdvancedFilters()">
  30. <span><i class="bi bi-funnel"></i> Filtres avancés</span>
  31. <i class="bi bi-chevron-up" id="advancedFiltersIcon"></i>
  32. </div>
  33. <div class="card-body" id="advancedFiltersBody">
  34. <div class="row">
  35. <!-- Filtre par mots-clés sur le titre -->
  36. <div class="col-lg-3 col-md-6 mb-3">
  37. <label class="form-label fw-bold">Mots-clés dans le Titre</label>
  38. <input type="text" class="form-control form-control-sm" id="search-titre-keywords" placeholder="Ex: facture contrat">
  39. <small class="text-muted">Mots-clés séparés par espaces</small>
  40. </div>
  41. <!-- Filtre par plage de dates -->
  42. <div class="col-lg-3 col-md-6 mb-3">
  43. <label class="form-label fw-bold">Période (Date)</label>
  44. <div class="d-flex align-items-center">
  45. <input type="date" id="dateDebut" class="form-control form-control-sm me-2">
  46. <span class="me-2">à</span>
  47. <input type="date" id="dateFin" class="form-control form-control-sm">
  48. </div>
  49. </div>
  50. <!-- Filtre par échéance -->
  51. <div class="col-lg-3 col-md-6 mb-3">
  52. <label class="form-label fw-bold">Période (Échéance)</label>
  53. <div class="d-flex align-items-center">
  54. <input type="date" id="deadlineDebut" class="form-control form-control-sm me-2">
  55. <span class="me-2">à</span>
  56. <input type="date" id="deadlineFin" class="form-control form-control-sm">
  57. </div>
  58. </div>
  59. </div>
  60. <div class="row">
  61. <!-- Filtre par montant -->
  62. <div class="col-lg-3 col-md-6 mb-3">
  63. <label class="form-label fw-bold">Recherche montant</label>
  64. <input type="text" class="form-control form-control-sm" id="searchMontant" placeholder="Ex: 5086 ou >1000">
  65. <small class="text-muted">Recherche exacte ou avec opérateur</small>
  66. </div>
  67. <!-- Filtre par plage de montant -->
  68. <div class="col-lg-3 col-md-6 mb-3">
  69. <label class="form-label fw-bold">Plage Montant</label>
  70. <div class="d-flex align-items-center">
  71. <input type="number" step="0.01" id="minMontant" class="form-control form-control-sm me-2" placeholder="Min" style="width: 100px;">
  72. <span class="me-2">à</span>
  73. <input type="number" step="0.01" id="maxMontant" class="form-control form-control-sm" placeholder="Max" style="width: 100px;">
  74. </div>
  75. </div>
  76. <!-- Filtre par tags -->
  77. <div class="col-lg-3 col-md-6 mb-3">
  78. <label class="form-label fw-bold">Tags</label>
  79. <input type="text" class="form-control form-control-sm" id="searchTags" placeholder="Ex: urgent important">
  80. <small class="text-muted">Mots-clés séparés par espaces</small>
  81. </div>
  82. </div>
  83. <div class="row">
  84. <!-- Filtre par Type -->
  85. <div class="col-lg-3 col-md-6 mb-3">
  86. <label class="form-label fw-bold">Type</label>
  87. <select class="form-select form-select-sm" id="filterType">
  88. <option value="">-- Tous les types --</option>
  89. </select>
  90. </div>
  91. <!-- Filtre par Attachement -->
  92. <div class="col-lg-3 col-md-6 mb-3">
  93. <label class="form-label fw-bold">Attachement</label>
  94. <select class="form-select form-select-sm" id="filterAttach">
  95. <option value="">-- Tous les attachements --</option>
  96. </select>
  97. </div>
  98. <!-- Filtre par Attribution -->
  99. <div class="col-lg-3 col-md-6 mb-3">
  100. <label class="form-label fw-bold">Attribution</label>
  101. <select class="form-select form-select-sm" id="filterAssign">
  102. <option value="">-- Toutes les attributions --</option>
  103. </select>
  104. </div>
  105. <!-- Filtre par Statut -->
  106. <div class="col-lg-3 col-md-6 mb-3">
  107. <label class="form-label fw-bold">Statut</label>
  108. <select class="form-select form-select-sm" id="filterStatut">
  109. <option value="">-- Tous les statuts --</option>
  110. </select>
  111. </div>
  112. </div>
  113. <div class="mt-2">
  114. <button type="button" class="btn btn-sm btn-primary me-2" onclick="applyAdvancedFilters()">
  115. <i class="bi bi-funnel-fill"></i> Appliquer les filtres
  116. </button>
  117. <button type="button" class="btn btn-sm btn-outline-secondary" onclick="clearAllAdvancedFilters()">
  118. <i class="bi bi-x-circle"></i> Réinitialiser les filtres
  119. </button>
  120. <span class="ms-3 text-muted" id="filterInfo"></span>
  121. </div>
  122. </div>
  123. </div>
  124. <style>
  125. #advancedFiltersPanel .card-header:hover {
  126. background-color: #f8f9fa;
  127. }
  128. </style>
  129. <div>
  130. <table
  131. id="table"
  132. class="table-striped table-hover table-sm"
  133. data-page-size="25"
  134. data-toggle="table"
  135. data-show-columns="true"
  136. data-search="true"
  137. data-buttons-align="left"
  138. data-show-footer="true"
  139. data-pagination="true"
  140. data-flat="true"
  141. data-sort-name="date"
  142. data-sort-order="desc"
  143. data-show-export="true"
  144. data-page-list="[25, 50, 100, 250, all]"
  145. data-url="<?php echo $jsonTarget ?>">
  146. <thead>
  147. <tr>
  148. <th data-sortable="true" data-field="date" data-width="90">Date</th>
  149. <th data-sortable="true" data-field="titre">Titre</th>
  150. <th data-sortable="true" data-field="label" data-width="125">Type</th>
  151. <th data-sortable="true" data-field="attach" data-width="180">Attachement</th>
  152. <th data-sortable="true" data-field="deadline" data-width="90">Echéance</th>
  153. <th data-sortable="true" data-field="tags" data-width="200">Tags</th>
  154. <th data-sortable="true" data-field="assign" data-width="200">Attribution</th>
  155. <th data-sortable="true" data-field="done" data-width="100">Statut</th>
  156. <th data-sortable="true" data-field="montant" data-width="110" data-formatter="dataFormatter" data-footer-formatter="montantFormatter">Montant</th>
  157. <th data-field="id" data-formatter="selectFormatter" data-width="60"></th>
  158. </tr>
  159. </thead>
  160. </table>
  161. </div>
  162. <script>
  163. <?php
  164. if (core::ifGet("tag")) { ?>
  165. $(function() {
  166. tagValue = "<?php echo core::getGet("tag"); ?>";
  167. if (tagValue) {
  168. function applyTagFilter() {
  169. var $input = $('#table input.bootstrap-table-filter-control-tags');
  170. console.log('input:', $input.length, 'valeur:', tagValue);
  171. if ($input.length) {
  172. $input.val(tagValue).trigger('input');
  173. }
  174. }
  175. $('#table').on('post-body.bs.table post-header.bs.table', applyTagFilter);
  176. setTimeout(applyTagFilter, 800);
  177. }
  178. });
  179. <?php
  180. }
  181. ?>
  182. function selectFormatter(value, row) {
  183. return '<a href="/document-' + row.id + '.html"><button type="submit" class="btn btn-outline-primary btn-sm">Ouvrir</button></a>';
  184. }
  185. let euro = Intl.NumberFormat('de-DE', {
  186. style: 'currency',
  187. currency: 'EUR',
  188. });
  189. function dataFormatter(value) {
  190. return euro.format(value);
  191. }
  192. function montantFormatter(data) {
  193. var total = 0;
  194. data.forEach(function(row) {
  195. total += parseFloat(row.montant);
  196. });
  197. return parseFloat(total) === 0 ? euro.format(0.00) : euro.format(total.toFixed(2));
  198. }
  199. // Données originales
  200. let originalData = [];
  201. // Toggle du panneau de filtres avancés
  202. function toggleAdvancedFilters() {
  203. const body = document.getElementById('advancedFiltersBody');
  204. const icon = document.getElementById('advancedFiltersIcon');
  205. if (body.style.display === 'none') {
  206. body.style.display = 'block';
  207. icon.className = 'bi bi-chevron-up';
  208. } else {
  209. body.style.display = 'none';
  210. icon.className = 'bi bi-chevron-down';
  211. }
  212. }
  213. // Convertir une date en objet Date (supporte DD/MM/YYYY et YYYY-MM-DD)
  214. function parseDate(dateStr) {
  215. if (!dateStr) return null;
  216. // Format DD/MM/YYYY
  217. if (dateStr.includes('/')) {
  218. const parts = dateStr.split('/');
  219. if (parts.length === 3) {
  220. return new Date(parseInt(parts[2]), parseInt(parts[1]) - 1, parseInt(parts[0]), 0, 0, 0);
  221. }
  222. }
  223. // Format YYYY-MM-DD (format ISO/MySQL)
  224. if (dateStr.includes('-')) {
  225. const parts = dateStr.split('-');
  226. if (parts.length === 3 && parts[0].length === 4) {
  227. return new Date(parseInt(parts[0]), parseInt(parts[1]) - 1, parseInt(parts[2]), 0, 0, 0);
  228. }
  229. }
  230. return null;
  231. }
  232. // Fonction de filtre pour les montants
  233. function filterMontant(searchValue, numValue) {
  234. if (!searchValue || searchValue.trim() === '') return true;
  235. const absNumValue = Math.abs(numValue);
  236. let searchNormalized = searchValue.trim()
  237. .replace(/\s/g, '')
  238. .replace(/€/g, '')
  239. .replace(/\./g, '')
  240. .replace(/,/g, '.');
  241. let operator = null;
  242. if (searchNormalized.startsWith('>=')) {
  243. operator = '>=';
  244. searchNormalized = searchNormalized.substring(2);
  245. } else if (searchNormalized.startsWith('<=')) {
  246. operator = '<=';
  247. searchNormalized = searchNormalized.substring(2);
  248. } else if (searchNormalized.startsWith('>')) {
  249. operator = '>';
  250. searchNormalized = searchNormalized.substring(1);
  251. } else if (searchNormalized.startsWith('<')) {
  252. operator = '<';
  253. searchNormalized = searchNormalized.substring(1);
  254. } else if (searchNormalized.startsWith('=')) {
  255. operator = '=';
  256. searchNormalized = searchNormalized.substring(1);
  257. }
  258. const searchNum = parseFloat(searchNormalized);
  259. if (isNaN(searchNum)) {
  260. const formattedValue = euro.format(numValue).toLowerCase();
  261. return formattedValue.includes(searchValue.toLowerCase());
  262. }
  263. if (operator) {
  264. switch (operator) {
  265. case '>':
  266. return numValue > searchNum;
  267. case '<':
  268. return numValue < searchNum;
  269. case '>=':
  270. return numValue >= searchNum;
  271. case '<=':
  272. return numValue <= searchNum;
  273. case '=':
  274. return Math.abs(numValue - searchNum) < 0.01;
  275. }
  276. }
  277. if (Math.abs(numValue - searchNum) < 0.01 || Math.abs(absNumValue - searchNum) < 0.01) {
  278. return true;
  279. }
  280. const intPart = Math.floor(absNumValue);
  281. const searchInt = Math.floor(searchNum);
  282. if (intPart === searchInt) {
  283. return true;
  284. }
  285. const numStr = absNumValue.toFixed(2);
  286. const searchStr = searchInt.toString();
  287. return numStr.startsWith(searchStr) || intPart.toString().includes(searchStr);
  288. }
  289. // Appliquer tous les filtres avancés
  290. function applyAdvancedFilters() {
  291. // Récupérer les dates
  292. const dateDebutStr = document.getElementById('dateDebut').value;
  293. const dateFinStr = document.getElementById('dateFin').value;
  294. const deadlineDebutStr = document.getElementById('deadlineDebut').value;
  295. const deadlineFinStr = document.getElementById('deadlineFin').value;
  296. let dateDebut = null,
  297. dateFin = null,
  298. deadlineDebut = null,
  299. deadlineFin = null;
  300. if (dateDebutStr) {
  301. const parts = dateDebutStr.split('-');
  302. dateDebut = new Date(parseInt(parts[0]), parseInt(parts[1]) - 1, parseInt(parts[2]), 0, 0, 0);
  303. }
  304. if (dateFinStr) {
  305. const parts = dateFinStr.split('-');
  306. dateFin = new Date(parseInt(parts[0]), parseInt(parts[1]) - 1, parseInt(parts[2]), 23, 59, 59);
  307. }
  308. if (deadlineDebutStr) {
  309. const parts = deadlineDebutStr.split('-');
  310. deadlineDebut = new Date(parseInt(parts[0]), parseInt(parts[1]) - 1, parseInt(parts[2]), 0, 0, 0);
  311. }
  312. if (deadlineFinStr) {
  313. const parts = deadlineFinStr.split('-');
  314. deadlineFin = new Date(parseInt(parts[0]), parseInt(parts[1]) - 1, parseInt(parts[2]), 23, 59, 59);
  315. }
  316. // Fonction pour normaliser le texte (retirer accents et passer en majuscules)
  317. const normalizeText = (text) => (text || '').normalize('NFD').replace(/[\u0300-\u036f]/g, '').toUpperCase();
  318. // Récupérer les mots-clés du titre
  319. const titreInput = document.getElementById('search-titre-keywords').value.trim();
  320. const titreKeywords = titreInput.split(/\s+/).filter(k => k.length > 0).map(k => normalizeText(k));
  321. // Récupérer les tags
  322. const tagsInput = document.getElementById('searchTags').value.trim();
  323. const tagsKeywords = tagsInput.split(/\s+/).filter(k => k.length > 0).map(k => normalizeText(k));
  324. // Récupérer la recherche de montant
  325. const searchMontant = document.getElementById('searchMontant').value.trim();
  326. const minMontant = parseFloat(document.getElementById('minMontant').value);
  327. const maxMontant = parseFloat(document.getElementById('maxMontant').value);
  328. // Récupérer les filtres Type, Attachement, Attribution, Statut
  329. const filterType = document.getElementById('filterType').value;
  330. const filterAttach = document.getElementById('filterAttach').value;
  331. const filterAssign = document.getElementById('filterAssign').value;
  332. const filterStatut = document.getElementById('filterStatut').value;
  333. let filteredData = originalData.filter(row => {
  334. // Filtre par plage de dates
  335. if (dateDebut || dateFin) {
  336. const rowDate = parseDate(row.date);
  337. if (!rowDate) return false;
  338. if (dateDebut && rowDate < dateDebut) return false;
  339. if (dateFin && rowDate > dateFin) return false;
  340. }
  341. // Filtre par plage d'échéance
  342. if (deadlineDebut || deadlineFin) {
  343. const rowDeadline = parseDate(row.deadline);
  344. if (!rowDeadline) return false;
  345. if (deadlineDebut && rowDeadline < deadlineDebut) return false;
  346. if (deadlineFin && rowDeadline > deadlineFin) return false;
  347. }
  348. // Filtre par mots-clés dans le titre
  349. if (titreKeywords.length > 0) {
  350. const titreNorm = normalizeText(row.titre);
  351. const hasAllKeywords = titreKeywords.every(keyword => titreNorm.includes(keyword));
  352. if (!hasAllKeywords) return false;
  353. }
  354. // Filtre par tags
  355. if (tagsKeywords.length > 0) {
  356. const tagsNorm = normalizeText(row.tags);
  357. const hasAllTags = tagsKeywords.every(keyword => tagsNorm.includes(keyword));
  358. if (!hasAllTags) return false;
  359. }
  360. // Filtre par recherche de montant
  361. if (searchMontant) {
  362. const montant = parseFloat(row.montant) || 0;
  363. if (!filterMontant(searchMontant, montant)) return false;
  364. }
  365. // Filtre par plage de montant
  366. const montant = parseFloat(row.montant) || 0;
  367. if (!isNaN(minMontant) && montant < minMontant) return false;
  368. if (!isNaN(maxMontant) && montant > maxMontant) return false;
  369. // Filtre par Type
  370. if (filterType && (row.label || '') !== filterType) return false;
  371. // Filtre par Attachement
  372. if (filterAttach && (row.attach || '') !== filterAttach) return false;
  373. // Filtre par Attribution
  374. if (filterAssign && (row.assign || '') !== filterAssign) return false;
  375. // Filtre par Statut
  376. if (filterStatut && (row.done || '') !== filterStatut) return false;
  377. return true;
  378. });
  379. // Recharger le tableau avec les données filtrées
  380. $('#table').bootstrapTable('load', filteredData);
  381. updateFilterInfo(filteredData.length, titreKeywords);
  382. }
  383. // Réinitialiser tous les filtres avancés
  384. function clearAllAdvancedFilters() {
  385. document.getElementById('dateDebut').value = '';
  386. document.getElementById('dateFin').value = '';
  387. document.getElementById('deadlineDebut').value = '';
  388. document.getElementById('deadlineFin').value = '';
  389. document.getElementById('search-titre-keywords').value = '';
  390. document.getElementById('searchTags').value = '';
  391. document.getElementById('searchMontant').value = '';
  392. document.getElementById('minMontant').value = '';
  393. document.getElementById('maxMontant').value = '';
  394. document.getElementById('filterType').value = '';
  395. document.getElementById('filterAttach').value = '';
  396. document.getElementById('filterAssign').value = '';
  397. document.getElementById('filterStatut').value = '';
  398. $('#table').bootstrapTable('load', originalData);
  399. updateFilterInfo(originalData.length, []);
  400. }
  401. // Mettre à jour l'info des filtres actifs
  402. function updateFilterInfo(count, keywords) {
  403. const infoEl = document.getElementById('filterInfo');
  404. const activeFilters = [];
  405. const dateDebut = document.getElementById('dateDebut').value;
  406. const dateFin = document.getElementById('dateFin').value;
  407. if (dateDebut || dateFin) {
  408. activeFilters.push(`Date: ${dateDebut || 'début'} à ${dateFin || 'fin'}`);
  409. }
  410. const deadlineDebut = document.getElementById('deadlineDebut').value;
  411. const deadlineFin = document.getElementById('deadlineFin').value;
  412. if (deadlineDebut || deadlineFin) {
  413. activeFilters.push(`Échéance: ${deadlineDebut || 'début'} à ${deadlineFin || 'fin'}`);
  414. }
  415. if (keywords && keywords.length > 0) {
  416. activeFilters.push(`Titre: "${keywords.join(' + ')}"`);
  417. }
  418. const searchTags = document.getElementById('searchTags').value;
  419. if (searchTags) {
  420. activeFilters.push(`Tags: "${searchTags}"`);
  421. }
  422. const searchMontant = document.getElementById('searchMontant').value;
  423. if (searchMontant) {
  424. activeFilters.push(`Montant: "${searchMontant}"`);
  425. }
  426. const minMontant = document.getElementById('minMontant').value;
  427. const maxMontant = document.getElementById('maxMontant').value;
  428. if (minMontant || maxMontant) {
  429. activeFilters.push(`Plage: ${minMontant || '0'} - ${maxMontant || '∞'}`);
  430. }
  431. const filterType = document.getElementById('filterType').value;
  432. if (filterType) {
  433. activeFilters.push(`Type: "${filterType}"`);
  434. }
  435. const filterAttach = document.getElementById('filterAttach').value;
  436. if (filterAttach) {
  437. activeFilters.push(`Attachement: "${filterAttach}"`);
  438. }
  439. const filterAssign = document.getElementById('filterAssign').value;
  440. if (filterAssign) {
  441. activeFilters.push(`Attribution: "${filterAssign}"`);
  442. }
  443. const filterStatut = document.getElementById('filterStatut').value;
  444. if (filterStatut) {
  445. activeFilters.push(`Statut: "${filterStatut}"`);
  446. }
  447. if (activeFilters.length > 0) {
  448. infoEl.innerHTML = `<strong>${count}</strong> document(s) affiché(s) | Filtres: ${activeFilters.join(', ')}`;
  449. } else {
  450. infoEl.innerHTML = `<strong>${count || originalData.length}</strong> document(s) au total`;
  451. }
  452. }
  453. // Populer les selects avec les valeurs uniques
  454. function populateSelectFilters(data) {
  455. const types = [...new Set(data.map(row => row.label).filter(v => v))].sort();
  456. const attachs = [...new Set(data.map(row => row.attach).filter(v => v))].sort();
  457. const assigns = [...new Set(data.map(row => row.assign).filter(v => v))].sort();
  458. const statuts = [...new Set(data.map(row => row.done).filter(v => v))].sort();
  459. const selectType = document.getElementById('filterType');
  460. const selectAttach = document.getElementById('filterAttach');
  461. const selectAssign = document.getElementById('filterAssign');
  462. const selectStatut = document.getElementById('filterStatut');
  463. // Réinitialiser et repeupler Type
  464. selectType.innerHTML = '<option value="">-- Tous les types --</option>';
  465. types.forEach(type => {
  466. const option = document.createElement('option');
  467. option.value = type;
  468. option.textContent = type;
  469. selectType.appendChild(option);
  470. });
  471. // Réinitialiser et repeupler Attachement
  472. selectAttach.innerHTML = '<option value="">-- Tous les attachements --</option>';
  473. attachs.forEach(attach => {
  474. const option = document.createElement('option');
  475. option.value = attach;
  476. option.textContent = attach;
  477. selectAttach.appendChild(option);
  478. });
  479. // Réinitialiser et repeupler Attribution
  480. selectAssign.innerHTML = '<option value="">-- Toutes les attributions --</option>';
  481. assigns.forEach(assign => {
  482. const option = document.createElement('option');
  483. option.value = assign;
  484. option.textContent = assign;
  485. selectAssign.appendChild(option);
  486. });
  487. // Réinitialiser et repeupler Statut
  488. selectStatut.innerHTML = '<option value="">-- Tous les statuts --</option>';
  489. statuts.forEach(statut => {
  490. const option = document.createElement('option');
  491. option.value = statut;
  492. option.textContent = statut;
  493. selectStatut.appendChild(option);
  494. });
  495. }
  496. // Initialisation
  497. $(document).ready(function() {
  498. const $table = $('#table');
  499. $table.on('load-success.bs.table', function(e, data) {
  500. originalData = data;
  501. populateSelectFilters(data);
  502. updateFilterInfo(data.length, []);
  503. });
  504. // Appliquer le filtre avec Entrée sur les champs texte
  505. ['search-titre-keywords', 'searchTags', 'searchMontant'].forEach(id => {
  506. document.getElementById(id).addEventListener('keypress', function(e) {
  507. if (e.key === 'Enter') {
  508. applyAdvancedFilters();
  509. }
  510. });
  511. });
  512. });
  513. </script>