/** * Script JavaScript pour l'interface d'administration B-PAY Bankily * Fichier: assets/bankily-admin.js * Version: 1.0.0 */ jQuery(document).ready(function($) { 'use strict'; // Variables globales var checking = false; var autoRefreshInterval; // Initialisation initializeAdminInterface(); function initializeAdminInterface() { // Gestion des checkboxes $('#cb-select-all').on('change', function() { $('input[name="transaction_ids[]"]').prop('checked', this.checked); updateBulkActionsState(); }); // Mise à jour de l'état des actions groupées $(document).on('change', 'input[name="transaction_ids[]"]', function() { updateBulkActionsState(); }); // Auto-refresh pour les transactions en attente if (window.location.href.indexOf('bankily-pending') > -1) { startAutoRefresh(); } // Initialiser les tooltips initializeTooltips(); // Gestion du temps réel updateTimeStamps(); setInterval(updateTimeStamps, 60000); // Mettre à jour chaque minute // Recherche en temps réel setupLiveSearch(); // Raccourcis clavier setupKeyboardShortcuts(); // Indicateur de connexion checkConnectionStatus(); setInterval(checkConnectionStatus, 30000); } function updateBulkActionsState() { var selectedCount = $('input[name="transaction_ids[]"]:checked').length; var $bulkButton = $('.button.action'); if (selectedCount > 0) { $bulkButton.prop('disabled', false).text('Appliquer (' + selectedCount + ')'); } else { $bulkButton.prop('disabled', true).text('Appliquer'); } } function initializeTooltips() { $('.error-message, [data-tooltip]').each(function() { var $this = $(this); var title = $this.attr('title') || $this.data('tooltip'); if (title) { $this.on('mouseenter', function(e) { showTooltip(e.pageX, e.pageY, title); }); $this.on('mouseleave', function() { hideTooltip(); }); $this.on('mousemove', function(e) { updateTooltipPosition(e.pageX, e.pageY); }); } }); } function showTooltip(x, y, text) { var $tooltip = $('#bankily-tooltip'); if ($tooltip.length === 0) { $tooltip = $('
').appendTo('body'); } $tooltip.text(text) .css({ position: 'absolute', left: x + 10, top: y - 30, background: '#333', color: '#fff', padding: '5px 10px', borderRadius: '4px', fontSize: '12px', zIndex: 9999, maxWidth: '300px', wordWrap: 'break-word' }) .fadeIn(200); } function hideTooltip() { $('#bankily-tooltip').fadeOut(200); } function updateTooltipPosition(x, y) { $('#bankily-tooltip').css({ left: x + 10, top: y - 30 }); } function updateTimeStamps() { $('.transaction-row').each(function() { var $row = $(this); var createdAt = $row.find('[data-timestamp]').data('timestamp'); if (createdAt) { var timeAgo = formatTimeAgo(new Date(createdAt * 1000)); $row.find('.time-ago').text(timeAgo); } }); } function formatTimeAgo(date) { var now = new Date(); var diffMs = now - date; var diffMins = Math.floor(diffMs / 60000); var diffHours = Math.floor(diffMins / 60); var diffDays = Math.floor(diffHours / 24); if (diffDays > 0) { return diffDays + ' jour' + (diffDays > 1 ? 's' : ''); } else if (diffHours > 0) { return diffHours + ' heure' + (diffHours > 1 ? 's' : ''); } else if (diffMins > 0) { return diffMins + ' minute' + (diffMins > 1 ? 's' : ''); } else { return 'À l\'instant'; } } function startAutoRefresh() { // Rafraîchir automatiquement toutes les 30 secondes si il y a des transactions en attente if ($('.bankily-status-ta').length > 0) { autoRefreshInterval = setTimeout(function() { if (!checking) { showNotice('info', 'Actualisation automatique...', 2000); setTimeout(function() { window.location.reload(); }, 1000); } }, 30000); } } function setupLiveSearch() { var searchTimeout; var $searchInput = $('input[name="search"]'); if ($searchInput.length) { $searchInput.on('input', function() { clearTimeout(searchTimeout); var query = $(this).val().toLowerCase(); searchTimeout = setTimeout(function() { filterTransactions(query); }, 300); }); } } function filterTransactions(query) { if (!query) { $('.transaction-row').show(); updateDisplayedCount(); return; } $('.transaction-row').each(function() { var $row = $(this); var text = $row.text().toLowerCase(); if (text.indexOf(query) !== -1) { $row.show(); } else { $row.hide(); } }); updateDisplayedCount(); } function updateDisplayedCount() { var visibleCount = $('.transaction-row:visible').length; $('.displaying-num').text(visibleCount + ' élément' + (visibleCount > 1 ? 's' : '')); } function setupKeyboardShortcuts() { $(document).on('keydown', function(e) { // Ctrl+R pour rafraîchir if (e.ctrlKey && e.keyCode === 82) { e.preventDefault(); window.location.reload(); } // Ctrl+A pour sélectionner tout if (e.ctrlKey && e.keyCode === 65 && $('.transaction-row').length > 0) { e.preventDefault(); $('#cb-select-all').prop('checked', true).trigger('change'); } // Échap pour désélectionner if (e.keyCode === 27) { $('#cb-select-all').prop('checked', false).trigger('change'); hideProgressBar(); } }); } function checkConnectionStatus() { $.ajax({ url: bankily_admin_ajax.ajax_url, type: 'POST', data: { action: 'bankily_ping', nonce: bankily_admin_ajax.nonce }, timeout: 5000, success: function() { updateConnectionStatus(true); }, error: function() { updateConnectionStatus(false); } }); } function updateConnectionStatus(isOnline) { var $indicator = $('#bankily-connection-status'); if ($indicator.length === 0) { $indicator = $('
').appendTo('body'); } $indicator.removeClass('online offline') .addClass(isOnline ? 'online' : 'offline') .text(isOnline ? '🟢 En ligne' : '🔴 Hors ligne'); } // Fonctions globales pour les boutons (exposées au scope global) window.bankilyCheckTransaction = function(operationId, transactionId) { if (checking) { return; } checking = true; var $button = $('button[onclick*="' + transactionId + '"]').first(); var originalText = $button.text(); var $row = $('#transaction-' + transactionId); $button.prop('disabled', true).addClass('bankily-loading').text(bankily_admin_ajax.strings.checking); $row.addClass('bankily-checking'); $.ajax({ url: bankily_admin_ajax.ajax_url, type: 'POST', data: { action: 'bankily_check_transaction', operation_id: operationId, transaction_id: transactionId, nonce: bankily_admin_ajax.nonce }, success: function(response) { if (response.success) { updateTransactionRow(transactionId, response.data); showNotice('success', response.data.message); // Animation de succès $row.addClass('bankily-success'); setTimeout(function() { $row.removeClass('bankily-success'); }, 2000); } else { showNotice('error', response.data); $row.addClass('bankily-error'); setTimeout(function() { $row.removeClass('bankily-error'); }, 2000); } }, error: function(xhr, status, error) { showNotice('error', 'Erreur de connexion: ' + error); $row.addClass('bankily-error'); setTimeout(function() { $row.removeClass('bankily-error'); }, 2000); }, complete: function() { $button.prop('disabled', false) .removeClass('bankily-loading') .text(originalText); $row.removeClass('bankily-checking'); checking = false; } }); }; window.bankilyCheckAllPending = function() { if (checking) { return; } var $pendingRows = $('.bankily-status-ta').closest('tr'); if ($pendingRows.length === 0) { showNotice('info', 'Aucune transaction en attente à vérifier.'); return; } if (!confirm('Vérifier toutes les ' + $pendingRows.length + ' transactions en attente ?\n\nCela peut prendre plusieurs minutes.')) { return; } checking = true; var transactionIds = []; $pendingRows.each(function() { var $checkbox = $(this).find('input[name="transaction_ids[]"]'); if ($checkbox.length > 0) { transactionIds.push($checkbox.val()); } }); if (transactionIds.length === 0) { checking = false; showNotice('warning', 'Impossible de récupérer les IDs des transactions.'); return; } var $progressBar = showProgressBar(transactionIds.length); // Traitement par lots pour éviter de surcharger le serveur processBatch(transactionIds, 0, $progressBar, 5); // Taille de lot : 5 }; function processBatch(transactionIds, startIndex, $progressBar, batchSize) { var batch = transactionIds.slice(startIndex, startIndex + batchSize); if (batch.length === 0) { hideProgressBar(); checking = false; showNotice('success', 'Vérification terminée pour toutes les transactions.'); // Rafraîchir la page après un délai setTimeout(function() { window.location.reload(); }, 2000); return; } updateProgressBar($progressBar, startIndex, transactionIds.length); $.ajax({ url: bankily_admin_ajax.ajax_url, type: 'POST', data: { action: 'bankily_bulk_check', transaction_ids: batch, nonce: bankily_admin_ajax.nonce }, success: function(response) { if (response.success) { response.data.forEach(function(result) { if (result.status === 'success') { updateTransactionRow(result.id, { status: result.new_status, status_label: getStatusLabel(result.new_status) }); } }); } // Délai entre les lots setTimeout(function() { processBatch(transactionIds, startIndex + batchSize, $progressBar, batchSize); }, 2000); // 2 secondes entre les lots }, error: function() { hideProgressBar(); checking = false; showNotice('error', 'Erreur lors de la vérification groupée.'); } }); } function updateTransactionRow(transactionId, data) { var $row = $('#transaction-' + transactionId); if (data.status) { var $statusCell = $row.find('.bankily-status'); $statusCell.removeClass('bankily-status-ts bankily-status-tf bankily-status-ta') .addClass('bankily-status-' + data.status.toLowerCase()) .text(data.status_label || getStatusLabel(data.status)); } if (data.transaction_id) { var $operationCell = $row.find('.operation-id'); if (!$operationCell.find('small').length) { $operationCell.append('
TXN: ' + data.transaction_id + ''); } } // Mettre à jour le compteur de tentatives var $attemptsCell = $row.find('td').eq(-2); var currentAttempts = parseInt($attemptsCell.text()) || 0; $attemptsCell.text(currentAttempts + 1); // Mettre à jour l'heure de dernière vérification var now = new Date(); var $lastCheckedCell = $row.find('td').eq(-3); $lastCheckedCell.text(formatDateTime(now)); // Animation de mise à jour $row.addClass('updated'); setTimeout(function() { $row.removeClass('updated'); }, 2000); } function getStatusLabel(status) { var labels = { 'TS': bankily_admin_ajax.strings.success, 'TF': bankily_admin_ajax.strings.failed, 'TA': bankily_admin_ajax.strings.pending }; return labels[status] || status; } function formatDateTime(date) { return date.toLocaleDateString('fr-FR', { day: '2-digit', month: '2-digit', year: 'numeric', hour: '2-digit', minute: '2-digit' }); } function showProgressBar(total) { var progressHtml = `

🔄 Vérification des transactions en cours...

0 / ${total}

⏱ Cette opération peut prendre plusieurs minutes. Veuillez patienter...

`; $('body').append(progressHtml); return $('#bankily-progress-overlay'); } function updateProgressBar($progressBar, current, total) { var percentage = (current / total) * 100; $progressBar.find('.bankily-progress-fill').css('width', percentage + '%'); $progressBar.find('.bankily-progress-text').text(current + ' / ' + total + ' (' + Math.round(percentage) + '%)'); } function hideProgressBar() { $('#bankily-progress-overlay').fadeOut(function() { $(this).remove(); }); } window.bankilyStopProcessing = function() { if (confirm('Êtes-vous sûr de vouloir arrêter le processus ?')) { checking = false; hideProgressBar(); showNotice('warning', 'Processus arrêté par l\'utilisateur.'); } }; window.bankilyBulkAction = function() { var action = $('#bulk-action-selector-top').val(); var selectedIds = []; $('input[name="transaction_ids[]"]:checked').each(function() { selectedIds.push($(this).val()); }); if (selectedIds.length === 0) { showNotice('warning', 'Veuillez sélectionner au moins une transaction.'); return; } if (!confirm(bankily_admin_ajax.strings.confirm_bulk + '\n\n' + selectedIds.length + ' transactions sélectionnées.')) { return; } switch (action) { case 'check': bankilyBulkCheck(selectedIds); break; case 'mark-failed': bankilyBulkMarkFailed(selectedIds); break; default: showNotice('warning', 'Veuillez sélectionner une action.'); } }; function bankilyBulkCheck(transactionIds) { if (checking) { return; } checking = true; var $progressBar = showProgressBar(transactionIds.length); processBatch(transactionIds, 0, $progressBar, 3); // Taille de lot réduite pour les actions manuelles } function bankilyBulkMarkFailed(transactionIds) { if (checking) { return; } checking = true; $.ajax({ url: bankily_admin_ajax.ajax_url, type: 'POST', data: { action: 'bankily_bulk_mark_failed', transaction_ids: transactionIds, nonce: bankily_admin_ajax.nonce }, success: function(response) { if (response.success) { transactionIds.forEach(function(id) { updateTransactionRow(id, { status: 'TF', status_label: getStatusLabel('TF') }); }); showNotice('success', response.data.message); } else { showNotice('error', 'Erreur lors du marquage des transactions.'); } }, error: function() { showNotice('error', bankily_admin_ajax.strings.error); }, complete: function() { checking = false; } }); } window.bankilyMarkAsFailed = function(transactionId) { if (!confirm('Êtes-vous sûr de vouloir marquer cette transaction comme échouée ?\n\nCette action est irréversible.')) { return; } bankilyBulkMarkFailed([transactionId]); }; window.bankilyExportTransactions = function() { showNotice('info', 'Préparation de l\'export...'); var url = bankily_admin_ajax.ajax_url + '?action=bankily_export_transactions&nonce=' + bankily_admin_ajax.nonce; // Créer un lien de téléchargement invisible var $link = $('').attr({ href: url, download: 'bankily-transactions-' + new Date().toISOString().split('T')[0] + '.csv' }).appendTo('body'); $link[0].click(); $link.remove(); showNotice('success', 'Export lancé. Le téléchargement va commencer.', 3000); }; window.bankilyCleanOldTransactions = function() { if (!confirm('Êtes-vous sûr de vouloir supprimer les transactions de plus de 6 mois ?\n\n⚠️ Cette action est irréversible !')) { return; } $.ajax({ url: bankily_admin_ajax.ajax_url, type: 'POST', data: { action: 'bankily_clean_old_transactions', nonce: bankily_admin_ajax.nonce }, success: function(response) { if (response.success) { showNotice('success', response.data.message); } else { showNotice('error', 'Erreur lors du nettoyage.'); } }, error: function() { showNotice('error', bankily_admin_ajax.strings.error); } }); }; function showNotice(type, message, autoHide = 5000) { // Supprimer les anciennes notices $('.bankily-notice').remove(); var noticeClass = 'notice-' + type; var icon = { 'success': '✅', 'error': '❌', 'warning': '⚠️', 'info': 'ℹ️' }[type] || ''; var noticeHtml = `

${icon} ${message}

`; // Ajouter la nouvelle notice $('.wrap h1').first().after(noticeHtml); // Gestion du bouton de fermeture $('.notice-dismiss').on('click', function() { $(this).closest('.notice').fadeOut(); }); // Auto-fermeture if (autoHide && autoHide > 0) { setTimeout(function() { $('.bankily-notice').fadeOut(); }, autoHide); } // Faire défiler vers la notice $('html, body').animate({ scrollTop: $('.bankily-notice').offset().top - 100 }, 300); } // Amélioration UX : Confirmation avant fermeture de page si traitement en cours window.addEventListener('beforeunload', function(e) { if (checking) { e.preventDefault(); e.returnValue = 'Une vérification est en cours. Êtes-vous sûr de vouloir quitter ?'; return e.returnValue; } }); // Nettoyage lors du déchargement de la page $(window).on('unload', function() { if (autoRefreshInterval) { clearTimeout(autoRefreshInterval); } checking = false; }); // Stats en temps réel (si sur le tableau de bord) if (window.location.href.indexOf('bankily-transactions') > -1 && window.location.href.indexOf('pending') === -1) { setInterval(function() { if (!checking) { updateDashboardStats(); } }, 60000); // Mettre à jour toutes les minutes } function updateDashboardStats() { $.ajax({ url: bankily_admin_ajax.ajax_url, type: 'POST', data: { action: 'bankily_get_dashboard_stats', nonce: bankily_admin_ajax.nonce }, success: function(response) { if (response.success) { updateStatCards(response.data); } }, error: function() { console.log('Erreur lors de la mise à jour des stats'); } }); } function updateStatCards(stats) { $('.bankily-stat-card.total h3').text(stats.total || 0); $('.bankily-stat-card.success h3').text(stats.success || 0); $('.bankily-stat-card.pending h3').text(stats.pending || 0); $('.bankily-stat-card.failed h3').text(stats.failed || 0); $('.bankily-stat-card.amount h3').text((stats.total_amount || 0).toLocaleString('fr-FR') + ' MRU'); } // Log d'initialisation console.log('[B-PAY Admin] Interface d\'administration initialisée', { checking: checking, page: window.location.href, ajax_url: bankily_admin_ajax.ajax_url }); });