// === Autopost client script === // --- CSRF global --- (function () { const meta = document.querySelector('meta[name="csrf-token"]'); const CSRF_TOKEN = meta ? meta.content : (window.__BOOTSTRAP__ && window.__BOOTSTRAP__.csrf) || ''; if (window.jQuery) { $.ajaxSetup({ headers: { 'x-csrf-token': CSRF_TOKEN } }); } })(); // --- XSS escape côté client --- function esc(s) { // ATTENTION: backtick échappé dans la classe de caractères: \` return String(s).replace(/[&<>"'\`=]/g, c => ({ '&': '&', '<': '<', '>': '>', '"': '"', "'": ''', '\`': '`', '=': '=' }[c])); } // --- Bootstrap values --- const INITIAL_PAGE = (window.__BOOTSTRAP__ && window.__BOOTSTRAP__.page) || 1; const INITIAL_TOTAL_PAGES= (window.__BOOTSTRAP__ && window.__BOOTSTRAP__.totalPages) || 1; const PAGE_LIMIT = (window.__BOOTSTRAP__ && window.__BOOTSTRAP__.limit) || 100; let currentPage = INITIAL_PAGE; let currentTotalPages = INITIAL_TOTAL_PAGES; let activeFilter = null; // number | null let activeQuery = ''; // string // --- Rendering: table --- function updateTable(rows) { var tbody = $('table tbody'); tbody.empty(); rows.forEach(function(row) { var statusText = ''; var statusClass = ''; switch (parseInt(row.status)) { case 0: statusText = 'EN ATTENTE'; statusClass = 'bg-cyan-500 text-black font-bold'; break; case 1: statusText = 'ENVOI TERMINÉ'; statusClass = 'bg-green-300 text-black font-bold'; break; case 2: statusText = 'ERREUR'; statusClass = 'bg-red-300 text-black font-bold'; break; case 3: statusText = 'DEJA DISPONIBLE';statusClass = 'bg-pink-300 text-black font-bold'; break; case 4: statusText = 'EN COURS'; statusClass = 'bg-yellow-300 text-black font-bold'; break; default: statusText = 'INCONNU'; } var logLink = (parseInt(row.status) === 1 || parseInt(row.status) === 2 || parseInt(row.status) === 4) ? ' | Log' : ''; var mediainfoLink = (parseInt(row.status) === 0 || parseInt(row.status) === 1 || parseInt(row.status) === 2) ? ' | Mediainfo' : ''; var dlLink = (parseInt(row.status) === 1) ? ' | DL' : ''; var tr = '' + '' + esc(row.nom) + '' + '' + statusText + '' + '' + row.id + '' + '' + 'Editer | ' + 'Supprimer' + logLink + mediainfoLink + dlLink + '' + ''; tbody.append(tr); }); } // --- Rendering: pagination --- function renderPaginationHTML(page, totalPages) { var prevDisabled = page <= 1; var nextDisabled = page >= totalPages; var h = ''; h += ''; return h; } function updatePagination(page, totalPages) { $('#pagination').html(renderPaginationHTML(page, totalPages)); } // --- Data loading --- function loadPage(p) { const targetPage = Math.max(1, parseInt(p, 10) || 1); const isFilter = activeFilter !== null; const url = isFilter ? '/autopost/filter' : '/autopost/search'; const data = isFilter ? { status: activeFilter, page: targetPage, limit: PAGE_LIMIT } : { q: activeQuery || '', page: targetPage, limit: PAGE_LIMIT }; $.ajax({ url: url, type: 'GET', data: data, dataType: 'json', success: function(resp) { updateTable(resp.rows); currentPage = resp.page; currentTotalPages = resp.totalPages; updatePagination(currentPage, currentTotalPages); }, error: function(jqXHR, textStatus, errorThrown) { if (textStatus !== 'abort') { console.error('Erreur AJAX :', textStatus, errorThrown); alert('Erreur lors du chargement de la page.'); } } }); } // --- Long polling: refresh table when new mediainfo/log apparaît --- (function () { var lastVersion = null; function poll() { $.ajax({ url: '/autopost/updates', type: 'GET', data: { since: lastVersion || 0 }, timeout: 30000, success: function (resp) { if (resp && typeof resp.version === 'number') { if (lastVersion === null) { lastVersion = resp.version; // 1ère synchro: on se cale } else if (resp.version > lastVersion) { lastVersion = resp.version; if (typeof loadPage === 'function') loadPage(currentPage || 1); if (typeof updateStatsUI === 'function') { $.getJSON('/autopost/stats', function (s) { if (s) updateStatsUI(s); }); } } } setTimeout(poll, 100); }, error: function () { setTimeout(poll, 2000); } }); } $(document).ready(poll); })(); function updateStatsUI(s) { $('.filter-card[data-status="0"] .tabular-nums').text(s.attente || 0); $('.filter-card[data-status="1"] .tabular-nums').text(s.termine || 0); $('.filter-card[data-status="2"] .tabular-nums').text(s.erreur || 0); $('.filter-card[data-status="3"] .tabular-nums').text(s.deja || 0); } function refreshAfterChange() { loadPage(currentPage || 1); $.getJSON('/autopost/stats', function(s) { updateStatsUI(s); }); } // --- DOM bindings --- $(document).ready(function() { // Recherche let searchTimer = null; $('#searchInput').on('keyup', function() { clearTimeout(searchTimer); let q = $(this).val(); searchTimer = setTimeout(function() { activeQuery = q || ''; activeFilter = null; // on sort du mode filtre $('.filter-card').removeClass('ring-4 ring-white/40'); loadPage(1); // charge page 1 avec pagination AJAX toggleShowAllButton(); }, 300); }); // Pagination: intercepter et paginer en AJAX $(document).on('click', '#pagination a[data-page]', function(e) { e.preventDefault(); const p = parseInt($(this).data('page'), 10); if (!isNaN(p)) loadPage(p); }); // Affichage/masquage du bouton "Tout afficher" function toggleShowAllButton() { if ($('.filter-card.ring-4').length === 0) { $('#showAll').addClass('hidden'); } else { $('#showAll').removeClass('hidden'); } } // Filtrage par clic sur une card $(document).on('click', '.filter-card', function() { const status = parseInt($(this).data('status'), 10); $('.filter-card').removeClass('ring-4 ring-white/40'); $(this).addClass('ring-4 ring-white/40'); activeFilter = status; activeQuery = ''; // on sort du mode recherche loadPage(1); // page 1 du filtre toggleShowAllButton(); }); // Bouton tout afficher $(document).on('click', '#showAll', function() { $('.filter-card').removeClass('ring-4 ring-white/40'); activeFilter = null; activeQuery = $('#searchInput').val() || ''; loadPage(1); toggleShowAllButton(); }); // Edition (ouvrir modale) $(document).on('click', '.edit-link', function(e) { e.preventDefault(); var releaseId = $(this).data('id'); var currentStatus = $(this).data('status'); $('#releaseId').val(releaseId); $('#modalReleaseId').text(releaseId); $('#statusSelect').val(currentStatus); $('#editModal').removeClass('hidden').fadeIn(); }); // ---------- Confirmation de suppression (modale) ---------- var pendingDeleteId = null; function openConfirmModal(id, name) { pendingDeleteId = id; $('#confirmItemName').text(name || ('ID ' + id)); $('#confirmDeleteModal').removeClass('hidden').fadeIn(120); } function closeConfirmModal() { $('#confirmDeleteModal').fadeOut(120, function() { $(this).addClass('hidden'); }); pendingDeleteId = null; } function showToast(msg) { $('#toastMsg').text(msg || 'Opération effectuée'); $('#toast').removeClass('hidden').fadeIn(120); setTimeout(function() { $('#toast').fadeOut(150, function() { $(this).addClass('hidden'); }); }, 1800); } // Ouvrir la modale au clic sur "Supprimer" $(document).on('click', '.delete-link', function(e) { e.preventDefault(); var releaseId = $(this).data('id'); var name = $(this).closest('tr').find('td:first').text().trim(); openConfirmModal(releaseId, name); }); // Boutons de la modale $('#cancelDeleteBtn, #confirmDeleteClose').on('click', function() { closeConfirmModal(); }); // Clic sur l’overlay pour fermer $('#confirmDeleteModal').on('click', function(e) { if (e.target === this) { closeConfirmModal(); } }); // Accessibilité clavier: Esc = fermer, Enter = confirmer $(document).on('keydown', function(e) { var modalVisible = !$('#confirmDeleteModal').hasClass('hidden'); if (!modalVisible) return; if (e.key === 'Escape') { closeConfirmModal(); } if (e.key === 'Enter') { $('#confirmDeleteBtn').click(); } }); // Confirmer et supprimer via AJAX $('#confirmDeleteBtn').on('click', function() { if (!pendingDeleteId) return; var releaseId = pendingDeleteId; $.ajax({ url: '/autopost/delete/' + releaseId, type: 'POST', success: function() { $('#row-' + releaseId) .css('outline', '2px solid rgba(239,68,68,0.6)') .fadeOut('300', function(){ $(this).remove(); }); showToast('Enregistrement supprimé'); }, error: function() { alert('Erreur lors de la suppression.'); }, complete: function() { closeConfirmModal(); } }); }); // Affichage log $(document).on('click', '.log-link', function(e) { e.preventDefault(); var filename = $(this).data('filename'); $.ajax({ url: '/autopost/log', type: 'GET', data: { name: filename }, dataType: 'json', success: function(data) { $('#logContent').html(data.content); $('#logModal').removeClass('hidden').fadeIn(); }, error: function() { alert('Erreur lors du chargement du fichier log.'); } }); }); // Affichage mediainfo $(document).on('click', '.mediainfo-link', function(e) { e.preventDefault(); var filename = $(this).data('filename'); $.ajax({ url: '/autopost/mediainfo', type: 'GET', data: { name: filename }, dataType: 'json', success: function(data) { $('#mediainfoContent').text(data.content); $('#mediainfoModal').removeClass('hidden').fadeIn(); }, error: function() { alert('Erreur lors du chargement du fichier mediainfo.'); } }); }); // Fermeture modales (croix + clic overlay) $('.close').click(function() { $(this).closest('.fixed').fadeOut(function() { $(this).addClass('hidden'); }); }); $('.fixed').click(function(e) { if (e.target === this) { $(this).fadeOut(function() { $(this).addClass('hidden'); }); } }); // Edition: submit $('#editForm').submit(function(e) { e.preventDefault(); var releaseId = $('#releaseId').val(); var newStatus = $('#statusSelect').val(); $.ajax({ url: '/autopost/edit/' + releaseId, type: 'POST', data: { status: newStatus }, success: function() { var statusText = ''; var statusClass = ''; switch (parseInt(newStatus)) { case 0: statusText = 'EN ATTENTE'; statusClass = 'bg-cyan-500 text-black font-bold'; break; case 1: statusText = 'ENVOI TERMINÉ'; statusClass = 'bg-green-300 text-black font-bold'; break; case 2: statusText = 'ERREUR'; statusClass = 'bg-red-300 text-black font-bold'; break; case 3: statusText = 'DEJA DISPONIBLE';statusClass = 'bg-pink-300 text-black font-bold'; break; case 4: statusText = 'EN COURS'; statusClass = 'bg-yellow-300 text-black font-bold'; break; default: statusText = 'INCONNU'; } var row = $('#row-' + releaseId); row.find('.status-text') .removeClass('bg-cyan-500 bg-green-300 bg-red-300 bg-pink-300 bg-yellow-300') .addClass(statusClass) .text(statusText); $('#editModal').fadeOut(function() { $(this).addClass('hidden'); }); }, error: function() { alert('Erreur lors de la mise à jour.'); } }); }); // Bouton copier Mediainfo $('#copyMediainfoBtn').on('click', function() { const content = document.getElementById('mediainfoContent').textContent; navigator.clipboard.writeText(content).then(() => { this.textContent = '✅ Copié !'; setTimeout(() => this.textContent = '📋 Copier JSON', 2000); }).catch(function(err) { alert('Erreur lors de la copie : ' + err); }); }); });