871 lines
33 KiB
JavaScript
871 lines
33 KiB
JavaScript
// Module principal de gestion de la modale d'événement
|
|
// Version refactorisée utilisant des modules séparés
|
|
|
|
import { createEvent, updateEvent, deleteEvent, changeEventStatus, getFilters, apiFetch } from './agenda-api.js';
|
|
import { notifyError, notifySuccess } from './agenda-notifications.js';
|
|
import { initializeEntityCreators } from './agenda-entity-creator.js';
|
|
|
|
// Modules refactorisés
|
|
import { clearDomCache, clearFormFields } from './agenda-modal-dom.js';
|
|
import { initializeModalButtons } from './agenda-modal-buttons.js';
|
|
import { initializeSelect2, populateSelects, filterTraducteursByLangue, getIsUpdatingSelects, clearDisponibilitesCache, initializeBeneficiaireListeRougeAlert, resetLangueSelectDisplay } from './agenda-modal-select.js';
|
|
import { fillFormWithDate, fillFormWithEvent, resetForm, showFormErrors, clearFormErrors, handleEventFormSubmit } from './agenda-modal-forms.js';
|
|
import { fillViewBlock, updateModalDisplay, checkIfEventIsPast } from './agenda-modal-display.js';
|
|
|
|
// Variables globales
|
|
let currentMode = 'view'; // 'view', 'edit', 'create'
|
|
let currentEventData = null;
|
|
let preservedMode = null;
|
|
let preservedEventData = null;
|
|
|
|
/**
|
|
* Préserve les données de la modale avant de l'ouvrir une sous-modale
|
|
*/
|
|
export function preserveModalData() {
|
|
preservedMode = currentMode;
|
|
preservedEventData = currentEventData;
|
|
}
|
|
|
|
/**
|
|
* Restaure les données de la modale après fermeture d'une sous-modale
|
|
* @returns {boolean} - True si des données ont été restaurées
|
|
*/
|
|
export function restoreModalData() {
|
|
if (preservedMode !== null && preservedEventData !== null) {
|
|
currentMode = preservedMode;
|
|
currentEventData = preservedEventData;
|
|
preservedMode = null;
|
|
preservedEventData = null;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Ouvre une sous-modale en gérant automatiquement la fermeture/réouverture de la modale principale
|
|
* @param {string} subModalId - ID de la sous-modale à ouvrir
|
|
* @param {Function} onBeforeOpen - Callback optionnel appelé avant l'ouverture (pour préparer les données)
|
|
* @param {Function} onAfterClose - Callback optionnel appelé après la fermeture de la sous-modale
|
|
* @param {number} delay - Délai avant ouverture de la sous-modale (ms, défaut: 300)
|
|
*/
|
|
export function openSubModal(subModalId, onBeforeOpen = null, onAfterClose = null, delay = 300) {
|
|
const subModal = document.getElementById(subModalId);
|
|
if (!subModal) {
|
|
return;
|
|
}
|
|
|
|
// Préserver les données de la modale principale
|
|
preserveModalData();
|
|
|
|
// Exécuter le callback de préparation si fourni
|
|
if (onBeforeOpen && typeof onBeforeOpen === 'function') {
|
|
try {
|
|
onBeforeOpen(subModal);
|
|
} catch (error) {
|
|
// Erreur silencieuse
|
|
}
|
|
}
|
|
|
|
// Fermer la modale principale
|
|
const eventModal = document.getElementById('eventModal');
|
|
if (eventModal) {
|
|
const eventBsModal = bootstrap.Modal.getInstance(eventModal);
|
|
if (eventBsModal) {
|
|
eventBsModal.hide();
|
|
}
|
|
}
|
|
|
|
// Attendre que la modale principale se ferme
|
|
setTimeout(() => {
|
|
// Ouvrir la sous-modale
|
|
if (window.bootstrap && window.bootstrap.Modal) {
|
|
const bsModal = new window.bootstrap.Modal(subModal);
|
|
bsModal.show();
|
|
|
|
// Gérer la réouverture de la modale principale à la fermeture
|
|
subModal.addEventListener('hidden.bs.modal', function handleSubModalClose() {
|
|
// Exécuter le callback de fermeture si fourni
|
|
if (onAfterClose && typeof onAfterClose === 'function') {
|
|
try {
|
|
onAfterClose(subModal);
|
|
} catch (error) {
|
|
// Erreur silencieuse
|
|
}
|
|
}
|
|
|
|
// Rouvrir la modale principale avec les données préservées
|
|
if (eventModal && window.bootstrap && window.bootstrap.Modal) {
|
|
const newEventModal = new window.bootstrap.Modal(eventModal);
|
|
newEventModal.show();
|
|
}
|
|
}, { once: true }); // Une seule fois
|
|
}
|
|
}, delay);
|
|
}
|
|
|
|
/**
|
|
* Charge les incidents d'un événement
|
|
* @param {number} eventId - ID de l'événement
|
|
* @returns {Promise<Array>} - Tableau des incidents
|
|
*/
|
|
async function loadEventIncidents(eventId) {
|
|
try {
|
|
const incidents = await apiFetch(`events/${eventId}/incidents`);
|
|
return incidents || [];
|
|
} catch (error) {
|
|
return [];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Ouvre la modal d'incidents en mode affichage
|
|
* Utilise la fonction générique openSubModal pour gérer la fermeture/réouverture de la modale principale
|
|
* @param {number} eventId - ID de l'événement
|
|
*/
|
|
async function openIncidentsViewModal(eventId) {
|
|
// Utiliser openSubModal pour gérer automatiquement la fermeture/réouverture
|
|
openSubModal(
|
|
'declarationIncidentModal',
|
|
// Callback avant ouverture : charger et afficher les incidents
|
|
async (subModal) => {
|
|
const modalTitle = document.getElementById('declarationIncidentModalLabel');
|
|
const viewSection = document.getElementById('incidentsViewSection');
|
|
const formSection = document.getElementById('incidentsFormSection');
|
|
const viewFooter = document.getElementById('incidentsViewFooter');
|
|
const formFooter = document.getElementById('incidentsFormFooter');
|
|
const listContainer = document.getElementById('incidentsListContainer');
|
|
|
|
if (!viewSection || !formSection || !listContainer) {
|
|
notifyError('Sections de la modal d\'incidents introuvables');
|
|
return;
|
|
}
|
|
|
|
try {
|
|
// Afficher un spinner de chargement
|
|
listContainer.innerHTML = `
|
|
<div class="text-center py-4">
|
|
<div class="spinner-border text-primary" role="status">
|
|
<span class="visually-hidden">Chargement...</span>
|
|
</div>
|
|
</div>
|
|
`;
|
|
|
|
// Charger les incidents
|
|
const incidents = await loadEventIncidents(eventId);
|
|
|
|
if (incidents.length === 0) {
|
|
listContainer.innerHTML = '<p class="text-muted">Aucun incident signalé pour cet événement.</p>';
|
|
} else {
|
|
// Afficher les incidents
|
|
let html = '<div class="list-group">';
|
|
incidents.forEach((incident, index) => {
|
|
const createdAt = incident.created_at ? new Date(incident.created_at).toLocaleDateString('fr-FR') : 'Date inconnue';
|
|
html += `
|
|
<div class="list-group-item">
|
|
<div class="d-flex w-100 justify-content-between">
|
|
<h6 class="mb-1">Incident #${index + 1}</h6>
|
|
<small class="text-muted">${createdAt}</small>
|
|
</div>
|
|
${incident.resume_incident ? `<p class="mb-1"><strong>Résumé :</strong> ${incident.resume_incident}</p>` : ''}
|
|
${incident.commentaire_incident ? `<p class="mb-0 text-muted">${incident.commentaire_incident}</p>` : ''}
|
|
</div>
|
|
`;
|
|
});
|
|
html += '</div>';
|
|
listContainer.innerHTML = html;
|
|
}
|
|
|
|
// Changer le titre de la modal
|
|
if (modalTitle) {
|
|
modalTitle.innerHTML = '<i class="fas fa-eye me-2"></i>Détail des incidents';
|
|
}
|
|
|
|
// Afficher la section de vue, masquer le formulaire
|
|
viewSection.style.display = 'block';
|
|
formSection.style.display = 'none';
|
|
viewFooter.style.display = 'block';
|
|
formFooter.style.display = 'none';
|
|
|
|
} catch (error) {
|
|
listContainer.innerHTML = '<p class="text-danger">Erreur lors du chargement des incidents</p>';
|
|
notifyError('Erreur lors du chargement des incidents');
|
|
}
|
|
},
|
|
// Callback après fermeture : réinitialiser au mode formulaire
|
|
(subModal) => {
|
|
const modalTitle = document.getElementById('declarationIncidentModalLabel');
|
|
const viewSection = document.getElementById('incidentsViewSection');
|
|
const formSection = document.getElementById('incidentsFormSection');
|
|
const viewFooter = document.getElementById('incidentsViewFooter');
|
|
const formFooter = document.getElementById('incidentsFormFooter');
|
|
const listContainer = document.getElementById('incidentsListContainer');
|
|
|
|
// Réinitialiser au mode formulaire
|
|
if (viewSection) viewSection.style.display = 'none';
|
|
if (formSection) formSection.style.display = 'block';
|
|
if (viewFooter) viewFooter.style.display = 'none';
|
|
if (formFooter) formFooter.style.display = 'block';
|
|
if (modalTitle) {
|
|
modalTitle.innerHTML = '<i class="fas fa-exclamation-triangle me-2"></i>Signaler un incident';
|
|
}
|
|
if (listContainer) {
|
|
listContainer.innerHTML = '';
|
|
}
|
|
}
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Vérifie si l'événement a des incidents et affiche le bouton en conséquence
|
|
* @param {number} eventId - ID de l'événement
|
|
*/
|
|
export async function checkAndDisplayIncidentsButton(eventId) {
|
|
const viewIncidentsBtn = document.getElementById('viewIncidentsBtn');
|
|
|
|
if (!viewIncidentsBtn) {
|
|
return;
|
|
}
|
|
|
|
try {
|
|
const incidents = await loadEventIncidents(eventId);
|
|
|
|
if (incidents.length > 0) {
|
|
viewIncidentsBtn.style.display = 'inline-block';
|
|
// Mettre à jour le texte du bouton avec le nombre
|
|
const icon = viewIncidentsBtn.querySelector('i');
|
|
const iconHtml = icon ? icon.outerHTML : '<i class="fas fa-eye me-1"></i>';
|
|
viewIncidentsBtn.innerHTML = `${iconHtml}Détail incident(s) (${incidents.length})`;
|
|
} else {
|
|
viewIncidentsBtn.style.display = 'none';
|
|
}
|
|
} catch (error) {
|
|
viewIncidentsBtn.style.display = 'none';
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Ouvre le modal d'événement
|
|
* @param {string} mode - Mode ('view', 'edit', 'create')
|
|
* @param {Object} eventData - Données de l'événement
|
|
*/
|
|
export function openModal(mode, eventData = null) {
|
|
// Vérifier s'il y a des données préservées à restaurer
|
|
if (restoreModalData()) {
|
|
mode = currentMode;
|
|
eventData = currentEventData;
|
|
} else {
|
|
if (eventData && !mode) {
|
|
mode = 'view';
|
|
}
|
|
currentMode = mode;
|
|
currentEventData = eventData;
|
|
}
|
|
|
|
const modal = document.getElementById('eventModal');
|
|
if (!modal) {
|
|
return;
|
|
}
|
|
|
|
// Obtenir ou créer l'instance Bootstrap du modal
|
|
let bsModal = window.bootstrap && window.bootstrap.Modal ? window.bootstrap.Modal.getInstance(modal) : null;
|
|
if (!bsModal && window.bootstrap && window.bootstrap.Modal) {
|
|
bsModal = new window.bootstrap.Modal(modal);
|
|
}
|
|
|
|
// S'assurer que le select langue a la classe skip-select2 AVANT toute initialisation
|
|
const langueSelect = document.getElementById('langue');
|
|
if (langueSelect && !langueSelect.classList.contains('skip-select2')) {
|
|
langueSelect.classList.add('skip-select2');
|
|
}
|
|
|
|
// Initialiser les composants
|
|
initializeEntityCreators();
|
|
initializeSelect2();
|
|
initializeTimeComfort();
|
|
initializeLangueFilter();
|
|
initializeBeneficiaireListeRougeAlert();
|
|
|
|
// Initialiser les boutons (une seule fois)
|
|
initializeModalButtons({
|
|
getCurrentEventData: () => currentEventData,
|
|
getCurrentMode: () => currentMode,
|
|
setCurrentMode: (mode) => { currentMode = mode; },
|
|
openSubModal: openSubModal,
|
|
openCheckPresenceModal: openCheckPresenceModal,
|
|
openIncidentsViewModal: openIncidentsViewModal,
|
|
enableDateSynchronization: enableDateSynchronization,
|
|
disableDateSynchronization: disableDateSynchronization,
|
|
onDeleted: () => disableDateSynchronization(),
|
|
onStatusChanged: () => disableDateSynchronization()
|
|
});
|
|
|
|
// Déterminer les permissions
|
|
const userCanEdit = window.crviPermissions && window.crviPermissions.can_edit;
|
|
const userCanCreate = window.crviPermissions && window.crviPermissions.can_create;
|
|
const userCanDelete = window.crviPermissions && window.crviPermissions.can_delete;
|
|
|
|
// Vérifier si l'événement est passé
|
|
const isEventPast = checkIfEventIsPast(eventData);
|
|
|
|
// Ajuster le mode selon les permissions
|
|
if (mode === 'edit' && !userCanEdit) {
|
|
mode = 'view';
|
|
currentMode = 'view';
|
|
}
|
|
if (mode === 'edit' && isEventPast) {
|
|
notifyError('Impossible de modifier un événement passé');
|
|
mode = 'view';
|
|
currentMode = 'view';
|
|
}
|
|
if (mode === 'create' && !userCanCreate) {
|
|
return;
|
|
}
|
|
|
|
// Mettre à jour l'affichage
|
|
updateModalDisplay(currentMode, userCanEdit, userCanDelete, currentEventData);
|
|
|
|
// Remplir les données selon le mode
|
|
if (mode === 'view') {
|
|
fillViewBlock(eventData);
|
|
clearFormErrors();
|
|
} else if (mode === 'edit') {
|
|
fillFormWithEvent(eventData, filterTraducteursByLangue);
|
|
populateSelects(eventData);
|
|
clearFormErrors();
|
|
} else if (mode === 'create') {
|
|
if (eventData && eventData.startStr) {
|
|
fillFormWithDate(eventData);
|
|
} else {
|
|
fillFormWithEvent(eventData, filterTraducteursByLangue);
|
|
}
|
|
populateSelects(eventData);
|
|
clearFormErrors();
|
|
enableDateSynchronization();
|
|
preventPastDates();
|
|
}
|
|
|
|
// Nettoyer les champs lors de la fermeture
|
|
modal.addEventListener('hidden.bs.modal', function() {
|
|
if (preservedMode === null && preservedEventData === null) {
|
|
clearModalFields();
|
|
clearDomCache();
|
|
clearDisponibilitesCache();
|
|
resetLangueSelectDisplay(); // Réinitialiser le display des options langue
|
|
currentMode = 'view';
|
|
currentEventData = null;
|
|
}
|
|
disableDateSynchronization();
|
|
}, { once: true });
|
|
|
|
// Afficher le modal
|
|
bsModal.show();
|
|
}
|
|
|
|
/**
|
|
* Ferme le modal de force (pour gérer les bugs de Bootstrap)
|
|
*/
|
|
function forceCloseModal() {
|
|
const modal = document.getElementById('eventModal');
|
|
if (modal) {
|
|
const bsModal = bootstrap.Modal.getInstance(modal);
|
|
if (bsModal) {
|
|
bsModal.hide();
|
|
}
|
|
}
|
|
|
|
// Nettoyer manuellement le backdrop et les classes
|
|
setTimeout(() => {
|
|
const backdrops = document.querySelectorAll('.modal-backdrop');
|
|
backdrops.forEach(backdrop => backdrop.remove());
|
|
document.body.classList.remove('modal-open');
|
|
document.body.style.overflow = '';
|
|
document.body.style.paddingRight = '';
|
|
}, 300);
|
|
}
|
|
|
|
/**
|
|
* Nettoie les champs du modal
|
|
*/
|
|
function clearModalFields() {
|
|
const textFields = [
|
|
'date_rdv', 'heure_rdv', 'date_fin', 'heure_fin',
|
|
'commentaire', 'nb_participants', 'nb_hommes', 'nb_femmes'
|
|
];
|
|
|
|
const selectFields = [
|
|
'type', 'langue', 'id_beneficiaire', 'id_intervenant',
|
|
'id_traducteur', 'id_local', 'id_departement', 'id_type_intervention'
|
|
];
|
|
|
|
clearFormFields(textFields, selectFields);
|
|
clearFormErrors();
|
|
|
|
// Réinitialiser les champs conditionnels
|
|
const typeField = document.getElementById('type');
|
|
if (typeField) {
|
|
typeField.dispatchEvent(new Event('change'));
|
|
}
|
|
}
|
|
|
|
// ========== Fonctions de synchronisation des dates/heures ==========
|
|
|
|
function calculateHeureFin(heureDebut) {
|
|
const [h, m] = heureDebut.split(':').map(Number);
|
|
const heureFin = ((h + 1) % 24).toString().padStart(2, '0') + ':' + m.toString().padStart(2, '0');
|
|
return heureFin;
|
|
}
|
|
|
|
function syncHeureFin() {
|
|
const heureRdvInput = document.getElementById('heure_rdv');
|
|
const heureFinInput = document.getElementById('heure_fin');
|
|
const typeInput = document.getElementById('type');
|
|
|
|
if (!heureRdvInput || !heureFinInput || !typeInput) return;
|
|
if (typeInput.value !== 'individuel') return;
|
|
|
|
const heureDebut = heureRdvInput.value;
|
|
if (!heureDebut) return;
|
|
|
|
const heureFinStr = calculateHeureFin(heureDebut);
|
|
heureFinInput.value = heureFinStr;
|
|
}
|
|
|
|
function syncDateFin() {
|
|
const dateRdvInput = document.getElementById('date_rdv');
|
|
const dateFinInput = document.getElementById('date_fin');
|
|
if (dateRdvInput && dateFinInput && dateRdvInput.value) {
|
|
dateFinInput.value = dateRdvInput.value;
|
|
}
|
|
}
|
|
|
|
function enableDateSynchronization() {
|
|
const dateRdvInput = document.getElementById('date_rdv');
|
|
const heureRdvInput = document.getElementById('heure_rdv');
|
|
const typeInput = document.getElementById('type');
|
|
|
|
if (dateRdvInput) {
|
|
dateRdvInput.removeEventListener('change', syncDateFin);
|
|
dateRdvInput.addEventListener('change', syncDateFin);
|
|
}
|
|
|
|
if (heureRdvInput && typeInput) {
|
|
heureRdvInput.removeEventListener('change', syncHeureFin);
|
|
heureRdvInput.addEventListener('change', syncHeureFin);
|
|
}
|
|
|
|
// Sélectionner 'individuel' par défaut en création
|
|
if (typeInput && currentMode === 'create' && !typeInput.value) {
|
|
typeInput.value = 'individuel';
|
|
typeInput.dispatchEvent(new Event('change'));
|
|
}
|
|
}
|
|
|
|
function disableDateSynchronization() {
|
|
const dateRdvInput = document.getElementById('date_rdv');
|
|
const heureRdvInput = document.getElementById('heure_rdv');
|
|
|
|
if (dateRdvInput) {
|
|
dateRdvInput.removeEventListener('change', syncDateFin);
|
|
}
|
|
if (heureRdvInput) {
|
|
heureRdvInput.removeEventListener('change', syncHeureFin);
|
|
}
|
|
}
|
|
|
|
function preventPastDates() {
|
|
const dateRdvInput = document.getElementById('date_rdv');
|
|
const dateFinInput = document.getElementById('date_fin');
|
|
|
|
if (dateRdvInput) {
|
|
const today = new Date().toISOString().split('T')[0];
|
|
dateRdvInput.setAttribute('min', today);
|
|
|
|
dateRdvInput.addEventListener('change', function() {
|
|
const selectedDate = this.value;
|
|
if (dateFinInput && selectedDate) {
|
|
dateFinInput.setAttribute('min', selectedDate);
|
|
if (dateFinInput.value && dateFinInput.value < selectedDate) {
|
|
dateFinInput.value = selectedDate;
|
|
}
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
// ========== Fonctions pour les fonctionnalités spécifiques ==========
|
|
|
|
function initializeTimeComfort() {
|
|
// Logique de confort pour les sélecteurs d'heure
|
|
// (inchangée de l'original)
|
|
}
|
|
|
|
function initializeLangueFilter() {
|
|
// Initialiser le filtre de langue
|
|
const langueSelect = document.getElementById('langue');
|
|
if (langueSelect) {
|
|
langueSelect.addEventListener('change', filterTraducteursByLangue);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Ouvre le modal de validation des présences pour un événement de groupe
|
|
* @param {Object} eventData - Données de l'événement
|
|
*/
|
|
export function openCheckPresenceModal(eventData) {
|
|
const modal = document.getElementById('eventCheckPresenceModal');
|
|
if (!modal) {
|
|
return;
|
|
}
|
|
|
|
// Récupérer les données de l'événement
|
|
const eventId = eventData?.id || eventData?.extendedProps?.id || null;
|
|
const nbParticipants = eventData?.nb_participants || eventData?.extendedProps?.nb_participants || 0;
|
|
const eventType = eventData?.type || eventData?.extendedProps?.type || '';
|
|
|
|
// Vérifier que c'est bien un événement de groupe
|
|
if (eventType !== 'groupe') {
|
|
return;
|
|
}
|
|
|
|
// Vérifier que nb_participants est valide
|
|
const numParticipants = parseInt(nbParticipants, 10);
|
|
if (!numParticipants || numParticipants < 1) {
|
|
notifyError('Le nombre de participants n\'est pas valide');
|
|
return;
|
|
}
|
|
|
|
// Remplir les informations de l'événement
|
|
const eventInfoEl = document.getElementById('presence_event_info');
|
|
if (eventInfoEl) {
|
|
const date = eventData?.date_rdv || eventData?.extendedProps?.date_rdv || '';
|
|
const heure = eventData?.heure_rdv || eventData?.extendedProps?.heure_rdv || '';
|
|
eventInfoEl.textContent = `Événement du ${date} à ${heure} - ${numParticipants} participant(s)`;
|
|
}
|
|
|
|
// Définir l'event_id dans le formulaire
|
|
const eventIdInput = document.getElementById('presence_event_id');
|
|
if (eventIdInput) {
|
|
eventIdInput.value = eventId;
|
|
}
|
|
|
|
// Générer les lignes de présence
|
|
const tbody = document.getElementById('presence_rows');
|
|
if (tbody) {
|
|
tbody.innerHTML = '';
|
|
for (let i = 0; i < numParticipants; i++) {
|
|
const row = document.createElement('tr');
|
|
row.innerHTML = `
|
|
<td>
|
|
<input type="checkbox" class="form-check-input" id="presence_${i}" name="presences[${i}][is_present]">
|
|
</td>
|
|
<td style="position: relative;">
|
|
<input type="text" class="form-control presence-nom-input" id="presence_nom_${i}" name="presences[${i}][nom]"
|
|
data-row-index="${i}" autocomplete="off" required>
|
|
<input type="hidden" id="presence_beneficiaire_id_${i}" name="presences[${i}][beneficiaire_id]">
|
|
<div class="autocomplete-suggestions" id="autocomplete_${i}" style="display: none;"></div>
|
|
</td>
|
|
<td>
|
|
<input type="text" class="form-control presence-prenom-input" id="presence_prenom_${i}" name="presences[${i}][prenom]"
|
|
data-row-index="${i}" autocomplete="off" required>
|
|
</td>
|
|
`;
|
|
tbody.appendChild(row);
|
|
}
|
|
|
|
// Initialiser l'autocomplétion pour tous les champs nom
|
|
initializePresenceAutocomplete();
|
|
}
|
|
|
|
// Fermer le modal principal d'événement s'il est ouvert
|
|
const eventModal = document.getElementById('eventModal');
|
|
if (eventModal) {
|
|
const eventBsModal = window.bootstrap && window.bootstrap.Modal ? window.bootstrap.Modal.getInstance(eventModal) : null;
|
|
if (eventBsModal) {
|
|
eventBsModal.hide();
|
|
}
|
|
}
|
|
|
|
// Attendre un peu que le modal principal se ferme avant d'ouvrir le nouveau
|
|
setTimeout(() => {
|
|
// Ouvrir le modal de validation des présences
|
|
if (window.bootstrap && window.bootstrap.Modal) {
|
|
const bsModal = new window.bootstrap.Modal(modal);
|
|
bsModal.show();
|
|
|
|
// Ajouter un event listener pour rouvrir le modal principal quand on ferme
|
|
modal.addEventListener('hidden.bs.modal', function() {
|
|
// Rouvrir le modal principal d'événement
|
|
if (eventModal && window.bootstrap && window.bootstrap.Modal) {
|
|
const newEventModal = new window.bootstrap.Modal(eventModal);
|
|
newEventModal.show();
|
|
}
|
|
}, { once: true }); // Une seule fois
|
|
}
|
|
}, 300); // Délai pour la transition
|
|
}
|
|
|
|
/**
|
|
* Fonction pour gérer la soumission du formulaire de présences
|
|
* @param {number} eventId - ID de l'événement
|
|
* @param {Array} presenceData - Données de présence
|
|
*/
|
|
async function handleGroupPresenceSubmission(eventId, presenceData) {
|
|
if (!eventId) {
|
|
notifyError('ID d\'événement manquant');
|
|
return;
|
|
}
|
|
|
|
if (!presenceData || !Array.isArray(presenceData) || presenceData.length === 0) {
|
|
notifyError('Aucune donnée de présence à enregistrer');
|
|
return;
|
|
}
|
|
|
|
// Valider les données
|
|
for (let i = 0; i < presenceData.length; i++) {
|
|
const presence = presenceData[i];
|
|
if (!presence.nom || !presence.prenom) {
|
|
notifyError(`Les champs nom et prénom sont requis pour le participant ${i + 1}`);
|
|
return;
|
|
}
|
|
}
|
|
|
|
try {
|
|
// Envoyer les données à l'API REST
|
|
const response = await apiFetch(`events/${eventId}/presences`, {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
},
|
|
body: JSON.stringify({
|
|
presences: presenceData
|
|
})
|
|
});
|
|
|
|
if (response && response.success) {
|
|
notifySuccess('Présences enregistrées avec succès');
|
|
|
|
// Fermer la modal
|
|
const modal = document.getElementById('eventCheckPresenceModal');
|
|
if (modal && window.bootstrap && window.bootstrap.Modal) {
|
|
const bsModal = window.bootstrap.Modal.getInstance(modal);
|
|
if (bsModal) {
|
|
bsModal.hide();
|
|
}
|
|
}
|
|
|
|
// Rafraîchir le calendrier si disponible
|
|
if (window.currentCalendar) {
|
|
window.currentCalendar.refetchEvents();
|
|
}
|
|
if (window.currentColleaguesCalendar) {
|
|
window.currentColleaguesCalendar.refetchEvents();
|
|
}
|
|
} else {
|
|
throw new Error(response?.data?.message || 'Erreur lors de l\'enregistrement des présences');
|
|
}
|
|
} catch (error) {
|
|
notifyError(error.message || 'Erreur lors de l\'enregistrement des présences');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Initialise l'autocomplétion pour les champs nom dans le formulaire de présences
|
|
*/
|
|
function initializePresenceAutocomplete() {
|
|
const nomInputs = document.querySelectorAll('.presence-nom-input');
|
|
|
|
nomInputs.forEach(nomInput => {
|
|
let searchTimeout = null;
|
|
let currentSuggestions = [];
|
|
|
|
const rowIndex = nomInput.getAttribute('data-row-index');
|
|
const prenomInput = document.getElementById(`presence_prenom_${rowIndex}`);
|
|
const beneficiaireIdInput = document.getElementById(`presence_beneficiaire_id_${rowIndex}`);
|
|
const suggestionsDiv = document.getElementById(`autocomplete_${rowIndex}`);
|
|
|
|
// Fonction pour rechercher des bénéficiaires
|
|
async function searchBeneficiaires(searchTerm) {
|
|
if (searchTerm.length < 5) {
|
|
suggestionsDiv.style.display = 'none';
|
|
return;
|
|
}
|
|
|
|
try {
|
|
const response = await apiFetch(`beneficiaires/search?search=${encodeURIComponent(searchTerm)}`, {
|
|
method: 'GET'
|
|
});
|
|
|
|
if (response && response.success && response.data) {
|
|
currentSuggestions = response.data;
|
|
displaySuggestions();
|
|
} else {
|
|
currentSuggestions = [];
|
|
suggestionsDiv.style.display = 'none';
|
|
}
|
|
} catch (error) {
|
|
currentSuggestions = [];
|
|
suggestionsDiv.style.display = 'none';
|
|
}
|
|
}
|
|
|
|
// Fonction pour afficher les suggestions
|
|
function displaySuggestions() {
|
|
if (currentSuggestions.length === 0) {
|
|
suggestionsDiv.style.display = 'none';
|
|
return;
|
|
}
|
|
|
|
suggestionsDiv.innerHTML = '';
|
|
currentSuggestions.forEach((beneficiaire) => {
|
|
const suggestionItem = document.createElement('div');
|
|
suggestionItem.className = 'autocomplete-item';
|
|
suggestionItem.textContent = `${beneficiaire.prenom} ${beneficiaire.nom}`;
|
|
suggestionItem.dataset.beneficiaireId = beneficiaire.id;
|
|
|
|
suggestionItem.addEventListener('click', () => {
|
|
selectBeneficiaire(beneficiaire);
|
|
});
|
|
|
|
suggestionsDiv.appendChild(suggestionItem);
|
|
});
|
|
|
|
// Positionner la div de suggestions sous le champ nom
|
|
const nomInputRect = nomInput.getBoundingClientRect();
|
|
const parentCell = nomInput.closest('td');
|
|
|
|
if (parentCell) {
|
|
// Position relative au td parent
|
|
suggestionsDiv.style.position = 'absolute';
|
|
suggestionsDiv.style.top = (nomInput.offsetTop + nomInput.offsetHeight) + 'px';
|
|
suggestionsDiv.style.left = nomInput.offsetLeft + 'px';
|
|
suggestionsDiv.style.width = nomInput.offsetWidth + 'px';
|
|
suggestionsDiv.style.zIndex = '1060';
|
|
}
|
|
|
|
suggestionsDiv.style.display = 'block';
|
|
}
|
|
|
|
// Fonction pour sélectionner un bénéficiaire
|
|
function selectBeneficiaire(beneficiaire) {
|
|
nomInput.value = beneficiaire.nom;
|
|
prenomInput.value = beneficiaire.prenom;
|
|
if (beneficiaireIdInput) {
|
|
beneficiaireIdInput.value = beneficiaire.id;
|
|
}
|
|
suggestionsDiv.style.display = 'none';
|
|
}
|
|
|
|
// Event listener sur le champ nom
|
|
nomInput.addEventListener('input', function() {
|
|
clearTimeout(searchTimeout);
|
|
const searchTerm = this.value.trim();
|
|
|
|
if (searchTerm.length >= 5) {
|
|
searchTimeout = setTimeout(() => {
|
|
searchBeneficiaires(searchTerm);
|
|
}, 300);
|
|
} else {
|
|
suggestionsDiv.style.display = 'none';
|
|
}
|
|
});
|
|
|
|
// Fermer les suggestions au clic en dehors
|
|
document.addEventListener('click', function(e) {
|
|
if (!nomInput.contains(e.target) && !suggestionsDiv.contains(e.target)) {
|
|
suggestionsDiv.style.display = 'none';
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
// ========== Initialisation des event listeners globaux ==========
|
|
|
|
// Rafraîchir les selects si date/heure/type change
|
|
['date_rdv', 'heure_rdv', 'type'].forEach(id => {
|
|
const element = document.getElementById(id);
|
|
if (element) {
|
|
element.addEventListener('change', () => {
|
|
if (getIsUpdatingSelects()) return;
|
|
if (currentMode === 'edit' || currentMode === 'create') {
|
|
populateSelects(currentEventData);
|
|
}
|
|
});
|
|
}
|
|
});
|
|
|
|
// Initialiser le bouton de sauvegarde des présences
|
|
const savePresencesBtn = document.getElementById('savePresencesBtn');
|
|
if (savePresencesBtn) {
|
|
savePresencesBtn.onclick = async function() {
|
|
const form = document.getElementById('eventCheckPresenceForm');
|
|
if (!form) {
|
|
notifyError('Formulaire de présences introuvable');
|
|
return;
|
|
}
|
|
|
|
const eventIdInput = document.getElementById('presence_event_id');
|
|
const eventId = eventIdInput ? parseInt(eventIdInput.value, 10) : null;
|
|
|
|
if (!eventId) {
|
|
notifyError('ID d\'événement manquant');
|
|
return;
|
|
}
|
|
|
|
// Récupérer toutes les lignes de présence
|
|
const rows = document.querySelectorAll('#presence_rows tr');
|
|
const presenceData = [];
|
|
|
|
rows.forEach((row, index) => {
|
|
const checkbox = row.querySelector(`input[type="checkbox"][id^="presence_${index}"]`);
|
|
const nomInput = row.querySelector(`input[id="presence_nom_${index}"]`);
|
|
const prenomInput = row.querySelector(`input[id="presence_prenom_${index}"]`);
|
|
const beneficiaireIdInput = row.querySelector(`input[id="presence_beneficiaire_id_${index}"]`);
|
|
|
|
if (nomInput && prenomInput) {
|
|
const nom = nomInput.value.trim();
|
|
const prenom = prenomInput.value.trim();
|
|
const isPresent = checkbox ? checkbox.checked : false;
|
|
const beneficiaireId = beneficiaireIdInput ? beneficiaireIdInput.value : null;
|
|
|
|
// Ne pas inclure les lignes vides (nom et prénom vides)
|
|
if (nom || prenom) {
|
|
const presenceItem = {
|
|
nom: nom,
|
|
prenom: prenom,
|
|
is_present: isPresent
|
|
};
|
|
|
|
// Ajouter beneficiaire_id si un bénéficiaire existant a été sélectionné
|
|
if (beneficiaireId) {
|
|
presenceItem.beneficiaire_id = parseInt(beneficiaireId, 10);
|
|
}
|
|
|
|
presenceData.push(presenceItem);
|
|
}
|
|
}
|
|
});
|
|
|
|
if (presenceData.length === 0) {
|
|
notifyError('Veuillez remplir au moins une ligne de présence');
|
|
return;
|
|
}
|
|
|
|
// Afficher un overlay de chargement
|
|
const modal = document.getElementById('eventCheckPresenceModal');
|
|
let overlayTarget = modal ? modal.querySelector('.modal-content') : null;
|
|
try {
|
|
if (window.CRVI_OVERLAY && overlayTarget) {
|
|
window.CRVI_OVERLAY.show(overlayTarget);
|
|
}
|
|
await handleGroupPresenceSubmission(eventId, presenceData);
|
|
} finally {
|
|
if (window.CRVI_OVERLAY && overlayTarget) {
|
|
window.CRVI_OVERLAY.hide(overlayTarget);
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
// Réexporter les fonctions nécessaires
|
|
export { fillFormWithDate, fillFormWithEvent, resetForm, showFormErrors, clearFormErrors, handleEventFormSubmit };
|