403 lines
13 KiB
JavaScript
403 lines
13 KiB
JavaScript
// Module ES6 pour le tableau de statistiques Agenda
|
|
import { apiFetch } from './agenda-api.js';
|
|
|
|
let currentPage = 1;
|
|
let currentFilters = {};
|
|
|
|
/**
|
|
* Initialise le module de tableau de stats
|
|
*/
|
|
export function initStatsTable() {
|
|
// Initialiser Select2 sur les filtres
|
|
if (typeof jQuery !== 'undefined' && jQuery.fn.select2) {
|
|
jQuery('.stats-filters .select2').select2({
|
|
width: '100%'
|
|
});
|
|
}
|
|
|
|
// Écouter le bouton de filtre
|
|
const filterBtn = document.getElementById('stats_filterBtn');
|
|
if (filterBtn) {
|
|
filterBtn.addEventListener('click', handleFilter);
|
|
}
|
|
|
|
// Écouter le bouton de réinitialisation
|
|
const resetBtn = document.getElementById('stats_resetFiltersBtn');
|
|
if (resetBtn) {
|
|
resetBtn.addEventListener('click', handleReset);
|
|
}
|
|
|
|
// Charger les événements au démarrage
|
|
loadEvents();
|
|
}
|
|
|
|
/**
|
|
* Collecte les filtres du formulaire
|
|
*/
|
|
function collectFilters() {
|
|
const filters = {};
|
|
|
|
// Date (convertir en date pour recherche exacte sur une journée)
|
|
const dateInput = document.getElementById('stats_date');
|
|
if (dateInput && dateInput.value) {
|
|
filters.date = dateInput.value;
|
|
}
|
|
|
|
// Local
|
|
const localSelect = document.getElementById('stats_local');
|
|
if (localSelect && localSelect.value) {
|
|
filters.local = localSelect.value;
|
|
}
|
|
|
|
// Personne (intervenant)
|
|
const personneSelect = document.getElementById('stats_personne');
|
|
if (personneSelect && personneSelect.value) {
|
|
filters.intervenant = personneSelect.value;
|
|
}
|
|
|
|
// Type d'intervention
|
|
const typeInterventionSelect = document.getElementById('stats_type_intervention');
|
|
if (typeInterventionSelect && typeInterventionSelect.value) {
|
|
filters.type_intervention = typeInterventionSelect.value;
|
|
}
|
|
|
|
// Bénéficiaire
|
|
const beneficiaireSelect = document.getElementById('stats_beneficiaire');
|
|
if (beneficiaireSelect && beneficiaireSelect.value) {
|
|
filters.beneficiaire = beneficiaireSelect.value;
|
|
}
|
|
|
|
// Langue
|
|
const langueSelect = document.getElementById('stats_langue');
|
|
if (langueSelect && langueSelect.value) {
|
|
filters.langue = langueSelect.value;
|
|
}
|
|
|
|
// Intervenant externe (traducteur)
|
|
const intervenantExterneSelect = document.getElementById('stats_intervenant_externe');
|
|
if (intervenantExterneSelect && intervenantExterneSelect.value) {
|
|
filters.traducteur = intervenantExterneSelect.value;
|
|
}
|
|
|
|
// Année
|
|
const anneeInput = document.getElementById('stats_annee');
|
|
if (anneeInput && anneeInput.value) {
|
|
filters.annee = parseInt(anneeInput.value, 10);
|
|
}
|
|
|
|
// Statut
|
|
const statutSelect = document.getElementById('stats_statut');
|
|
if (statutSelect && statutSelect.value) {
|
|
filters.statut = statutSelect.value;
|
|
}
|
|
|
|
// Filtre permanence
|
|
const permanenceCheckbox = document.getElementById('stats_filtre_permanence');
|
|
if (permanenceCheckbox && permanenceCheckbox.checked) {
|
|
filters.type = 'permanence';
|
|
}
|
|
|
|
return filters;
|
|
}
|
|
|
|
/**
|
|
* Gère le clic sur le bouton Filtrer
|
|
*/
|
|
function handleFilter() {
|
|
currentPage = 1;
|
|
loadEvents();
|
|
}
|
|
|
|
/**
|
|
* Gère le clic sur le bouton Réinitialiser
|
|
*/
|
|
function handleReset() {
|
|
// Réinitialiser tous les champs
|
|
const form = document.querySelector('.stats-filters');
|
|
if (form) {
|
|
form.reset();
|
|
// Réinitialiser Select2
|
|
if (typeof jQuery !== 'undefined' && jQuery.fn.select2) {
|
|
jQuery('.stats-filters .select2').val(null).trigger('change');
|
|
}
|
|
}
|
|
currentPage = 1;
|
|
currentFilters = {};
|
|
loadEvents();
|
|
}
|
|
|
|
/**
|
|
* Charge les événements depuis l'API
|
|
*/
|
|
async function loadEvents() {
|
|
const loadingIndicator = document.getElementById('stats-loading-indicator');
|
|
const table = document.getElementById('stats-events-table');
|
|
const tableBody = document.getElementById('stats-events-table-body');
|
|
const paginationContainer = document.getElementById('stats-pagination-container');
|
|
const tableWrapper = document.getElementById('stats-table-wrapper');
|
|
|
|
// Afficher le loading et masquer le tableau
|
|
if (loadingIndicator) {
|
|
loadingIndicator.style.display = 'flex';
|
|
}
|
|
if (tableWrapper) {
|
|
tableWrapper.style.display = 'none';
|
|
}
|
|
if (table) {
|
|
table.style.display = 'none';
|
|
}
|
|
if (paginationContainer) {
|
|
paginationContainer.style.display = 'none';
|
|
}
|
|
|
|
try {
|
|
// Collecter les filtres
|
|
currentFilters = collectFilters();
|
|
|
|
// Ajouter la pagination
|
|
const params = {
|
|
...currentFilters,
|
|
page: currentPage,
|
|
per_page: 20
|
|
};
|
|
|
|
// Appel API
|
|
const result = await apiFetch(`events/table?${new URLSearchParams(params).toString()}`);
|
|
|
|
// Afficher les résultats
|
|
displayEvents(result.events || []);
|
|
// Afficher le nombre d'événements filtrés comme total, et le nombre d'événements sur la page courante comme affichés
|
|
updateCounters(result.filtered || 0, (result.events || []).length);
|
|
displayPagination(result.page || 1, result.total_pages || 0);
|
|
|
|
// IMPORTANT: Masquer le loader EN PREMIER, puis afficher le tableau
|
|
if (loadingIndicator) {
|
|
loadingIndicator.style.display = 'none';
|
|
}
|
|
|
|
// Afficher le conteneur du tableau
|
|
if (tableWrapper) {
|
|
tableWrapper.style.display = 'block';
|
|
}
|
|
|
|
// Afficher le tableau
|
|
if (table) {
|
|
table.style.display = 'table';
|
|
}
|
|
|
|
// Afficher la pagination si nécessaire
|
|
if (paginationContainer && result.total_pages > 0) {
|
|
paginationContainer.style.display = 'block';
|
|
}
|
|
|
|
} catch (error) {
|
|
console.error('Erreur lors du chargement des événements:', error);
|
|
|
|
// En cas d'erreur, masquer le loader et afficher le tableau avec le message d'erreur
|
|
if (loadingIndicator) {
|
|
loadingIndicator.style.display = 'none';
|
|
}
|
|
|
|
if (tableWrapper) {
|
|
tableWrapper.style.display = 'block';
|
|
}
|
|
|
|
if (table) {
|
|
table.style.display = 'table';
|
|
}
|
|
|
|
if (tableBody) {
|
|
tableBody.innerHTML = `<tr><td colspan="8" style="text-align: center; color: red; padding: 20px;">Erreur lors du chargement des données</td></tr>`;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Affiche les événements dans le tableau
|
|
*/
|
|
function displayEvents(events) {
|
|
const tableBody = document.getElementById('stats-events-table-body');
|
|
if (!tableBody) return;
|
|
|
|
if (events.length === 0) {
|
|
tableBody.innerHTML = '<tr><td colspan="8" style="text-align: center;">Aucun événement trouvé</td></tr>';
|
|
return;
|
|
}
|
|
|
|
let html = '';
|
|
events.forEach(event => {
|
|
const dateHeure = event.date_rdv && event.heure_rdv
|
|
? `${formatDate(event.date_rdv)} ${event.heure_rdv}`
|
|
: '-';
|
|
|
|
const statutLabel = formatStatutLabel(event.statut);
|
|
const statutClass = getStatutBadgeClass(event.statut);
|
|
|
|
html += `
|
|
<tr>
|
|
<td style="font-weight: 500; color: #495057;">${event.id || '-'}</td>
|
|
<td>${dateHeure}</td>
|
|
<td>${event.intervenant_nom || '-'}</td>
|
|
<td>${event.beneficiaire_nom || '-'}</td>
|
|
<td>${event.traducteur_nom || '-'}</td>
|
|
<td>${event.langue || '-'}</td>
|
|
<td><span class="badge badge-${statutClass}">${statutLabel}</span></td>
|
|
<td style="max-width: 200px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;" title="${escapeHtml(event.commentaire || '')}">${event.commentaire || '-'}</td>
|
|
</tr>
|
|
`;
|
|
});
|
|
|
|
tableBody.innerHTML = html;
|
|
}
|
|
|
|
/**
|
|
* Formate une date au format français
|
|
*/
|
|
function formatDate(dateString) {
|
|
if (!dateString) return '-';
|
|
const date = new Date(dateString + 'T00:00:00');
|
|
return date.toLocaleDateString('fr-FR', {
|
|
year: 'numeric',
|
|
month: '2-digit',
|
|
day: '2-digit'
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Formate le label du statut pour l'affichage
|
|
*/
|
|
function formatStatutLabel(statut) {
|
|
if (!statut) return '-';
|
|
const statutMap = {
|
|
'prevu': 'Prévu',
|
|
'annule': 'Annulé',
|
|
'non_tenu': 'Non tenu',
|
|
'cloture': 'Clôturé',
|
|
'absence': 'Absence'
|
|
};
|
|
return statutMap[statut.toLowerCase()] || statut;
|
|
}
|
|
|
|
/**
|
|
* Retourne la classe Bootstrap pour le badge de statut
|
|
*/
|
|
function getStatutBadgeClass(statut) {
|
|
if (!statut) return 'secondary';
|
|
const statutMap = {
|
|
'prevu': 'success',
|
|
'annule': 'danger',
|
|
'non_tenu': 'warning',
|
|
'cloture': 'secondary',
|
|
'absence': 'warning'
|
|
};
|
|
return statutMap[statut.toLowerCase()] || 'secondary';
|
|
}
|
|
|
|
/**
|
|
* Échappe les caractères HTML pour éviter les injections XSS
|
|
*/
|
|
function escapeHtml(text) {
|
|
if (!text) return '';
|
|
const div = document.createElement('div');
|
|
div.textContent = text;
|
|
return div.innerHTML;
|
|
}
|
|
|
|
/**
|
|
* Met à jour les compteurs
|
|
*/
|
|
function updateCounters(total, filtered) {
|
|
const totalCount = document.getElementById('total-count');
|
|
const filteredCount = document.getElementById('filtered-count');
|
|
|
|
if (totalCount) {
|
|
totalCount.textContent = total;
|
|
}
|
|
if (filteredCount) {
|
|
filteredCount.textContent = filtered;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Affiche la pagination (uniquement les numéros de page)
|
|
*/
|
|
function displayPagination(currentPageNum, totalPages) {
|
|
const paginationContainer = document.getElementById('stats-pagination-container');
|
|
if (!paginationContainer || totalPages <= 1) {
|
|
if (paginationContainer) {
|
|
paginationContainer.style.display = 'none';
|
|
}
|
|
return;
|
|
}
|
|
|
|
// Afficher le conteneur
|
|
paginationContainer.style.display = 'block';
|
|
|
|
let html = '<nav aria-label="Pagination" style="display: flex; justify-content: center; align-items: center;"><ul class="pagination" style="margin: 0; display: flex; list-style: none; gap: 5px; flex-wrap: wrap;">';
|
|
|
|
// Afficher toutes les pages si peu nombreuses, sinon un sous-ensemble
|
|
const maxPagesToShow = 10;
|
|
|
|
if (totalPages <= maxPagesToShow) {
|
|
// Afficher toutes les pages
|
|
for (let i = 1; i <= totalPages; i++) {
|
|
if (i === currentPageNum) {
|
|
html += `<li class="page-item active"><span class="page-link" style="padding: 8px 12px; border: 1px solid #007bff; border-radius: 4px; background: #007bff; color: #fff; font-weight: 600;">${i}</span></li>`;
|
|
} else {
|
|
html += `<li class="page-item"><a class="page-link" href="#" data-page="${i}" style="padding: 8px 12px; border: 1px solid #dee2e6; border-radius: 4px; text-decoration: none; color: #007bff; background: #fff; cursor: pointer;">${i}</a></li>`;
|
|
}
|
|
}
|
|
} else {
|
|
// Afficher un sous-ensemble de pages avec ellipses
|
|
let startPage = Math.max(1, currentPageNum - Math.floor(maxPagesToShow / 2));
|
|
let endPage = Math.min(totalPages, startPage + maxPagesToShow - 1);
|
|
|
|
if (endPage - startPage < maxPagesToShow - 1) {
|
|
startPage = Math.max(1, endPage - maxPagesToShow + 1);
|
|
}
|
|
|
|
// Première page
|
|
if (startPage > 1) {
|
|
html += `<li class="page-item"><a class="page-link" href="#" data-page="1" style="padding: 8px 12px; border: 1px solid #dee2e6; border-radius: 4px; text-decoration: none; color: #007bff; background: #fff; cursor: pointer;">1</a></li>`;
|
|
if (startPage > 2) {
|
|
html += `<li class="page-item disabled"><span class="page-link" style="padding: 8px 12px; color: #6c757d;">...</span></li>`;
|
|
}
|
|
}
|
|
|
|
// Pages du milieu
|
|
for (let i = startPage; i <= endPage; i++) {
|
|
if (i === currentPageNum) {
|
|
html += `<li class="page-item active"><span class="page-link" style="padding: 8px 12px; border: 1px solid #007bff; border-radius: 4px; background: #007bff; color: #fff; font-weight: 600;">${i}</span></li>`;
|
|
} else {
|
|
html += `<li class="page-item"><a class="page-link" href="#" data-page="${i}" style="padding: 8px 12px; border: 1px solid #dee2e6; border-radius: 4px; text-decoration: none; color: #007bff; background: #fff; cursor: pointer;">${i}</a></li>`;
|
|
}
|
|
}
|
|
|
|
// Dernière page
|
|
if (endPage < totalPages) {
|
|
if (endPage < totalPages - 1) {
|
|
html += `<li class="page-item disabled"><span class="page-link" style="padding: 8px 12px; color: #6c757d;">...</span></li>`;
|
|
}
|
|
html += `<li class="page-item"><a class="page-link" href="#" data-page="${totalPages}" style="padding: 8px 12px; border: 1px solid #dee2e6; border-radius: 4px; text-decoration: none; color: #007bff; background: #fff; cursor: pointer;">${totalPages}</a></li>`;
|
|
}
|
|
}
|
|
|
|
html += '</ul></nav>';
|
|
|
|
paginationContainer.innerHTML = html;
|
|
|
|
// Ajouter les écouteurs d'événements
|
|
const pageLinks = paginationContainer.querySelectorAll('a[data-page]');
|
|
pageLinks.forEach(link => {
|
|
link.addEventListener('click', (e) => {
|
|
e.preventDefault();
|
|
const page = parseInt(link.getAttribute('data-page'), 10);
|
|
if (page && page !== currentPage) {
|
|
currentPage = page;
|
|
loadEvents();
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|