// Module de gestion des boutons de la modale // Contient les gestionnaires d'événements pour tous les boutons de la modale import { deleteEvent, changeEventStatus, apiFetch } from './agenda-api.js'; import { notifyError, notifySuccess } from './agenda-notifications.js'; import { populateSelects } from './agenda-modal-select.js'; import { fillFormWithEvent, clearFormErrors, handleEventFormSubmit } from './agenda-modal-forms.js'; import { updateModalDisplay, fillViewBlock } from './agenda-modal-display.js'; /** * Helper pour gérer l'overlay de chargement * @param {Function} asyncAction - Action asynchrone à exécuter * @returns {Promise} */ async function withLoadingOverlay(asyncAction) { const overlayTarget = document.querySelector('#eventModal .modal-content') || document.getElementById('eventModal'); try { if (window.CRVI_OVERLAY && overlayTarget) { window.CRVI_OVERLAY.show(overlayTarget); } await asyncAction(); } finally { if (window.CRVI_OVERLAY && overlayTarget) { window.CRVI_OVERLAY.hide(overlayTarget); } } } /** * Helper pour rafraîchir les calendriers */ function refreshCalendars() { if (window.currentCalendar) { window.currentCalendar.refetchEvents(); } if (window.currentColleaguesCalendar) { window.currentColleaguesCalendar.refetchEvents(); } } /** * Helper pour fermer le modal * @param {Function} onClosed - Callback après fermeture */ function closeModal(onClosed = null) { const modal = document.getElementById('eventModal'); if (modal) { const bsModal = bootstrap.Modal.getInstance(modal); if (bsModal) { if (onClosed) { modal.addEventListener('hidden.bs.modal', onClosed, { once: true }); } bsModal.hide(); } } } /** * Vérifie les permissions utilisateur * @param {string} permission - Type de permission ('can_edit', 'can_delete', 'can_create') * @returns {boolean} */ function checkPermission(permission) { const hasPermission = window.crviPermissions && window.crviPermissions[permission]; if (!hasPermission) { console.warn(`Utilisateur non autorisé: ${permission}`); } return hasPermission; } /** * Initialise le bouton de fermeture * @param {string} buttonId - ID du bouton */ export function initializeCloseButton(buttonId) { const button = document.getElementById(buttonId); if (button) { button.onclick = () => closeModal(); } } /** * Initialise le bouton de suppression * @param {Function} getCurrentEventData - Fonction pour obtenir les données de l'événement * @param {Function} onDeleted - Callback après suppression */ export function initializeDeleteButton(getCurrentEventData, onDeleted = null) { const deleteBtn = document.getElementById('deleteEvent'); if (!deleteBtn) return; deleteBtn.onclick = async function() { if (!checkPermission('can_delete')) return; if (!confirm('Êtes-vous sûr de vouloir supprimer cet événement ?')) { return; } const currentEventData = getCurrentEventData(); const eventId = currentEventData ? currentEventData.id : null; if (!eventId) { notifyError('ID d\'événement manquant'); return; } await withLoadingOverlay(async () => { try { await deleteEvent(eventId); refreshCalendars(); closeModal(onDeleted); } catch (error) { console.error('Erreur lors de la suppression:', error); notifyError('Erreur lors de la suppression de l\'événement'); } }); }; } /** * Initialise le bouton de validation de présence * @param {Function} getCurrentEventData - Fonction pour obtenir les données de l'événement * @param {Function} openCheckPresenceModal - Fonction pour ouvrir la modal de validation de présence de groupe * @param {Function} onStatusChanged - Callback après changement de statut */ export function initializeMarkPresentButton(getCurrentEventData, openCheckPresenceModal, onStatusChanged = null) { const markPresentBtn = document.getElementById('markPresentBtn'); if (!markPresentBtn) return; markPresentBtn.onclick = async function() { if (!checkPermission('can_edit')) return; const currentEventData = getCurrentEventData(); const eventId = currentEventData ? currentEventData.id : null; if (!eventId) { notifyError('ID d\'événement manquant'); return; } // Vérifier le type d'événement const eventType = currentEventData?.type || currentEventData?.extendedProps?.type || ''; const isGroupe = eventType === 'groupe'; if (isGroupe) { // Pour les événements de groupe, ouvrir la modal de validation des présences openCheckPresenceModal(currentEventData); } else { // Pour les événements individuels if (!confirm('Confirmer la présence à cet événement ?')) { return; } await withLoadingOverlay(async () => { try { await changeEventStatus(eventId, 'present'); notifySuccess('Présence validée'); refreshCalendars(); closeModal(onStatusChanged); } catch (error) { console.error('Erreur lors du changement de statut:', error); notifyError('Erreur lors de la validation de présence'); } }); } }; } /** * Initialise le bouton pour marquer comme absent * @param {Function} getCurrentEventData - Fonction pour obtenir les données de l'événement * @param {Function} onStatusChanged - Callback après changement de statut */ export function initializeMarkAbsentButton(getCurrentEventData, onStatusChanged = null) { const markAbsentBtn = document.getElementById('markAbsentBtn'); if (!markAbsentBtn) return; markAbsentBtn.onclick = async function() { if (!checkPermission('can_edit')) return; const currentEventData = getCurrentEventData(); const eventId = currentEventData ? currentEventData.id : null; if (!eventId) { notifyError('ID d\'événement manquant'); return; } if (!confirm('Marquer cet événement comme absent ?')) { return; } await withLoadingOverlay(async () => { try { await changeEventStatus(eventId, 'absence'); notifySuccess('Événement marqué comme absent'); refreshCalendars(); closeModal(onStatusChanged); } catch (error) { console.error('Erreur lors du changement de statut:', error); notifyError('Erreur lors du changement de statut'); } }); }; } /** * Initialise le bouton d'annulation de rendez-vous * @param {Function} getCurrentEventData - Fonction pour obtenir les données de l'événement * @param {Function} onCancelled - Callback après annulation */ export function initializeCancelAppointmentButton(getCurrentEventData, onCancelled = null) { const cancelAppointmentBtn = document.getElementById('cancelAppointmentBtn'); if (!cancelAppointmentBtn) return; cancelAppointmentBtn.onclick = async function() { if (!checkPermission('can_edit')) return; const currentEventData = getCurrentEventData(); const eventId = currentEventData ? currentEventData.id : null; if (!eventId) { notifyError('ID d\'événement manquant'); return; } const motif = prompt('Motif de l\'annulation (optionnel):'); if (motif === null) { return; // L'utilisateur a cliqué sur Annuler } await withLoadingOverlay(async () => { try { await changeEventStatus(eventId, 'annule', motif); notifySuccess('Rendez-vous annulé'); refreshCalendars(); closeModal(onCancelled); } catch (error) { console.error('Erreur lors de l\'annulation:', error); notifyError('Erreur lors de l\'annulation du rendez-vous'); } }); }; } /** * Initialise le bouton Edit * @param {Function} getCurrentEventData - Fonction pour obtenir les données de l'événement * @param {Function} getCurrentMode - Fonction pour obtenir le mode actuel * @param {Function} setCurrentMode - Fonction pour changer le mode * @param {Function} enableDateSynchronization - Fonction pour activer la synchronisation des dates */ export function initializeEditButton(getCurrentEventData, getCurrentMode, setCurrentMode, enableDateSynchronization) { const editBtn = document.getElementById('editEventBtn'); if (!editBtn) return; editBtn.onclick = async function() { if (!checkPermission('can_edit')) return; const currentEventData = getCurrentEventData(); // Changer le mode setCurrentMode('edit'); // Mettre à jour l'affichage const userCanEdit = window.crviPermissions && window.crviPermissions.can_edit; const userCanDelete = window.crviPermissions && window.crviPermissions.can_delete; updateModalDisplay('edit', userCanEdit, userCanDelete, currentEventData); // Remplir le formulaire avec les données actuelles fillFormWithEvent(currentEventData); // Peupler les selects avec overlay de chargement await withLoadingOverlay(async () => { try { await populateSelects(currentEventData); } catch (error) { console.error('Erreur lors du peuplement des selects:', error); } }); clearFormErrors(); // Activer la synchronisation des dates if (enableDateSynchronization) { enableDateSynchronization(); } }; } /** * Initialise le bouton Cancel * @param {Function} getCurrentEventData - Fonction pour obtenir les données de l'événement * @param {Function} getCurrentMode - Fonction pour obtenir le mode actuel * @param {Function} setCurrentMode - Fonction pour changer le mode * @param {Function} disableDateSynchronization - Fonction pour désactiver la synchronisation des dates */ export function initializeCancelButton(getCurrentEventData, getCurrentMode, setCurrentMode, disableDateSynchronization) { const cancelBtn = document.getElementById('cancelEditBtn'); if (!cancelBtn) return; cancelBtn.onclick = function() { const currentMode = getCurrentMode(); const currentEventData = getCurrentEventData(); if (currentMode === 'create') { // En mode création, fermer le modal closeModal(); } else if (currentMode === 'edit' && currentEventData) { // En mode édition, retourner en mode vue setCurrentMode('view'); const userCanEdit = window.crviPermissions && window.crviPermissions.can_edit; const userCanDelete = window.crviPermissions && window.crviPermissions.can_delete; updateModalDisplay('view', userCanEdit, userCanDelete, currentEventData); // Réafficher les données en mode lecture fillViewBlock(currentEventData); clearFormErrors(); // Désactiver la synchronisation des dates if (disableDateSynchronization) { disableDateSynchronization(); } } }; } /** * Initialise le bouton Save * @param {Function} getCurrentEventData - Fonction pour obtenir les données de l'événement * @param {Function} getCurrentMode - Fonction pour obtenir le mode actuel * @param {Function} disableDateSynchronization - Fonction pour désactiver la synchronisation des dates */ export function initializeSaveButton(getCurrentEventData, getCurrentMode, disableDateSynchronization) { const saveBtn = document.getElementById('saveEvent'); if (!saveBtn) return; saveBtn.onclick = async function() { const currentMode = getCurrentMode(); const currentEventData = getCurrentEventData(); const eventId = currentEventData ? currentEventData.id : null; await withLoadingOverlay(async () => { await handleEventFormSubmit(currentMode, eventId, function() { // Succès : fermer le modal et rafraîchir les calendriers refreshCalendars(); closeModal(); // Désactiver la synchronisation des dates if (disableDateSynchronization) { disableDateSynchronization(); } }); }); }; } /** * Initialise le bouton "Signaler un incident" * @param {Function} getCurrentEventData - Fonction pour obtenir les données de l'événement * @param {Function} openSubModal - Fonction pour ouvrir une sous-modale */ export function initializeReportIncidentButton(getCurrentEventData, openSubModal) { const reportIncidentBtn = document.getElementById('reportIncidentBtn'); if (!reportIncidentBtn) return; reportIncidentBtn.onclick = async function() { const currentEventData = getCurrentEventData(); if (!currentEventData || !currentEventData.id) { notifyError('Aucun événement sélectionné'); return; } // Récupérer le type d'événement const eventType = currentEventData.type || currentEventData.extendedProps?.type || 'individuel'; const isGroupe = eventType === 'groupe'; // Ouvrir la sous-modale avec préparation des données openSubModal( 'declarationIncidentModal', (subModal) => { // Pré-remplir le formulaire const eventIdInput = document.getElementById('incident_event_id'); const eventTypeInput = document.getElementById('incident_event_type'); const sectionIndividuel = document.getElementById('incident_individuel_section'); const sectionGroupe = document.getElementById('incident_groupe_section'); if (eventIdInput) eventIdInput.value = currentEventData.id; if (eventTypeInput) eventTypeInput.value = eventType; // Afficher/masquer les sections selon le type if (isGroupe) { if (sectionIndividuel) sectionIndividuel.style.display = 'none'; if (sectionGroupe) sectionGroupe.style.display = 'block'; } else { if (sectionIndividuel) sectionIndividuel.style.display = 'block'; if (sectionGroupe) sectionGroupe.style.display = 'none'; // Pré-remplir les infos du bénéficiaire const beneficiaireId = currentEventData.id_beneficiaire || currentEventData.extendedProps?.id_beneficiaire; const beneficiaireNom = currentEventData.beneficiaire?.nom_complet || currentEventData.extendedProps?.beneficiaire?.nom_complet || 'Non spécifié'; const beneficiaireNomInput = document.getElementById('incident_beneficiaire_nom'); const beneficiaireIdInput = document.getElementById('incident_beneficiaire_id'); if (beneficiaireNomInput) beneficiaireNomInput.value = beneficiaireNom; if (beneficiaireIdInput) beneficiaireIdInput.value = beneficiaireId || ''; } } ); }; } /** * Charge l'historique des 3 derniers rendez-vous d'un bénéficiaire * @param {number} beneficiaireId - ID du bénéficiaire * @returns {Promise} Tableau des rendez-vous avec leurs incidents */ async function loadBeneficiaireHistorique(beneficiaireId) { try { const historique = await apiFetch(`beneficiaires/${beneficiaireId}/historique`); return historique || []; } catch (error) { console.error('Erreur lors du chargement de l\'historique:', error); notifyError('Erreur lors du chargement de l\'historique du bénéficiaire'); return []; } } /** * Affiche l'historique sous forme de ligne du temps verticale * @param {Array} historiqueData - Tableau des rendez-vous avec leurs incidents */ function displayHistoriqueTimeline(historiqueData) { const timelineContainer = document.getElementById('historiqueTimeline'); if (!timelineContainer) { console.error('Conteneur de timeline non trouvé'); return; } if (!historiqueData || historiqueData.length === 0) { timelineContainer.innerHTML = `
Aucun rendez-vous trouvé pour ce bénéficiaire.
`; return; } let html = '
'; historiqueData.forEach((rdv, index) => { const date = new Date(rdv.date_rdv + 'T' + rdv.heure_rdv); const dateFormatted = date.toLocaleDateString('fr-FR', { day: '2-digit', month: '2-digit', year: 'numeric' }); const heureFormatted = date.toLocaleTimeString('fr-FR', { hour: '2-digit', minute: '2-digit' }); const intervenantNom = rdv.intervenant ? `${rdv.intervenant.nom || ''} ${rdv.intervenant.prenom || ''}`.trim() : 'Non renseigné'; const typeInterventionNom = rdv.type_intervention?.nom || 'Non renseigné'; // Déterminer le type d'événement et la couleur const hasIncident = rdv.incident && rdv.incident.id; const statutLower = rdv.statut ? rdv.statut.toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, "") : ''; let eventType = 'default'; let icon = 'fa-calendar-alt'; if (hasIncident || statutLower.includes('absence') || statutLower.includes('absent')) { eventType = 'incident'; // Rouge icon = 'fa-exclamation-triangle'; } else if (statutLower.includes('present')) { eventType = 'present'; // Vert icon = 'fa-check-circle'; } else if (statutLower.includes('prevu')) { eventType = 'prevu'; // Bleu icon = 'fa-clock'; } else { eventType = 'default'; // Gris clair icon = 'fa-calendar-alt'; } const isLastItem = index === historiqueData.length - 1; html += `
${dateFormatted}
${heureFormatted}
${hasIncident ? '' : ''} ${rdv.type || 'Rendez-vous'}
Intervenant: ${intervenantNom}
Type d'intervention: ${typeInterventionNom}
${rdv.statut ? `
Statut: ${rdv.statut}
` : ''} ${rdv.commentaire ? `
Commentaire: ${rdv.commentaire}
` : ''} ${hasIncident ? `
Incident signalé
Résumé: ${rdv.incident.resume_incident || 'Non renseigné'}
${rdv.incident.commentaire_incident ? `
Commentaire: ${rdv.incident.commentaire_incident}
` : ''}
` : ''}
`; }); html += '
'; timelineContainer.innerHTML = html; } /** * Initialise le bouton "Historique bénéficiaire" * @param {Function} getCurrentEventData - Fonction pour obtenir les données de l'événement * @param {Function} openSubModal - Fonction pour ouvrir une sous-modale */ export function initializeHistoriqueButton(getCurrentEventData, openSubModal) { const historiqueBtn = document.getElementById('showBeneficiaireHistoriqueBtn'); if (!historiqueBtn) return; historiqueBtn.onclick = async function() { const currentEventData = getCurrentEventData(); const beneficiaireId = historiqueBtn.getAttribute('data-benef') || currentEventData?.id_beneficiaire || currentEventData?.extendedProps?.id_beneficiaire; if (!beneficiaireId) { notifyError('ID du bénéficiaire introuvable'); return; } // Ouvrir la sous-modale avec chargement des données openSubModal( 'beneficiaireHistoriqueModal', async (subModal) => { // Afficher le spinner de chargement const timelineContainer = document.getElementById('historiqueTimeline'); if (timelineContainer) { timelineContainer.innerHTML = `
Chargement...
`; } // Charger l'historique try { const historiqueData = await loadBeneficiaireHistorique(parseInt(beneficiaireId)); displayHistoriqueTimeline(historiqueData); } catch (error) { console.error('Erreur lors du chargement de l\'historique:', error); if (timelineContainer) { timelineContainer.innerHTML = `
Erreur lors du chargement de l'historique.
`; } } }, (subModal) => { // Nettoyer après fermeture const timelineContainer = document.getElementById('historiqueTimeline'); if (timelineContainer) { timelineContainer.innerHTML = ''; } } ); }; } /** * Initialise le bouton "Debug SMS" * @param {Function} getCurrentEventData - Fonction pour obtenir les données de l'événement */ export function initializeDebugSmsButton(getCurrentEventData) { const debugSmsBtn = document.getElementById('debugSmsBtn'); if (!debugSmsBtn) return; debugSmsBtn.onclick = function() { const currentEventData = getCurrentEventData(); const eventId = currentEventData ? currentEventData.id : null; console.log('[DEBUG_SMS] Données événement actuelles:', currentEventData); // Construire un message simple pour le debug const dateStr = (document.getElementById('date_rdv') && document.getElementById('date_rdv').value) || currentEventData?.date || ''; const timeStr = (document.getElementById('heure_rdv') && document.getElementById('heure_rdv').value) || currentEventData?.time || ''; const typeStr = (document.getElementById('type') && document.getElementById('type').value) || currentEventData?.type || ''; const msg = `Debug SMS - Event ${eventId ?? 'N/A'} - ${dateStr} ${timeStr} - type: ${typeStr}`; const phone = '0485500723'; if (!window.crviAjax || !window.crviAjax.url || !window.crviAjax.nonce) { alert('Debug SMS: configuration AJAX manquante.'); return; } const params = new URLSearchParams(); params.append('action', 'crvi_send_sms_debug'); params.append('nonce', window.crviAjax.nonce); params.append('phone', phone); params.append('message', msg); if (eventId) { params.append('id_event', String(eventId)); } params.append('sujet', 'debug'); fetch(window.crviAjax.url, { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' }, body: params.toString() }).then(async (res) => { let json; try { json = await res.json(); } catch (_) {} if (!res.ok || !json?.success) { const errMsg = json?.data?.message || 'Erreur lors de l\'envoi SMS (debug).'; throw new Error(errMsg); } alert('SMS de debug envoyé.'); }).catch((err) => { console.error('[DEBUG_SMS] Échec envoi:', err); alert('Échec envoi SMS de debug: ' + (err?.message || 'Erreur inconnue')); }); }; } /** * Initialise le bouton "Voir les incidents" * @param {Function} getCurrentEventData - Fonction pour obtenir les données de l'événement * @param {Function} openIncidentsViewModal - Fonction pour ouvrir la modal de visualisation des incidents */ export function initializeViewIncidentsButton(getCurrentEventData, openIncidentsViewModal) { const viewIncidentsBtn = document.getElementById('viewIncidentsBtn'); if (!viewIncidentsBtn) return; viewIncidentsBtn.onclick = async function() { const currentEventData = getCurrentEventData(); const eventId = currentEventData?.id || currentEventData?.extendedProps?.id; if (!eventId) { notifyError('ID de l\'événement introuvable'); return; } await openIncidentsViewModal(eventId); }; } /** * Initialise tous les boutons de la modale * @param {Object} options - Options de configuration * @param {Function} options.getCurrentEventData - Fonction pour obtenir les données de l'événement * @param {Function} options.getCurrentMode - Fonction pour obtenir le mode actuel * @param {Function} options.setCurrentMode - Fonction pour changer le mode * @param {Function} options.openSubModal - Fonction pour ouvrir une sous-modale * @param {Function} options.openCheckPresenceModal - Fonction pour ouvrir la modal de validation de présence * @param {Function} options.openIncidentsViewModal - Fonction pour ouvrir la modal de visualisation des incidents * @param {Function} options.enableDateSynchronization - Fonction pour activer la synchronisation des dates * @param {Function} options.disableDateSynchronization - Fonction pour désactiver la synchronisation des dates * @param {Function} options.onDeleted - Callback après suppression * @param {Function} options.onStatusChanged - Callback après changement de statut */ export function initializeModalButtons(options = {}) { const { getCurrentEventData, getCurrentMode, setCurrentMode, openSubModal, openCheckPresenceModal, openIncidentsViewModal, enableDateSynchronization, disableDateSynchronization, onDeleted, onStatusChanged } = options; // Boutons de fermeture initializeCloseButton('closeModalBtn'); initializeCloseButton('closeViewBtn'); // Boutons d'action if (getCurrentEventData) { initializeEditButton(getCurrentEventData, getCurrentMode, setCurrentMode, enableDateSynchronization); initializeCancelButton(getCurrentEventData, getCurrentMode, setCurrentMode, disableDateSynchronization); initializeSaveButton(getCurrentEventData, getCurrentMode, disableDateSynchronization); initializeDeleteButton(getCurrentEventData, onDeleted); initializeMarkPresentButton(getCurrentEventData, openCheckPresenceModal, onStatusChanged); initializeMarkAbsentButton(getCurrentEventData, onStatusChanged); initializeCancelAppointmentButton(getCurrentEventData, onStatusChanged); initializeDebugSmsButton(getCurrentEventData); // Boutons spécifiques avec sous-modales if (openSubModal) { initializeReportIncidentButton(getCurrentEventData, openSubModal); initializeHistoriqueButton(getCurrentEventData, openSubModal); } // Bouton de visualisation des incidents if (openIncidentsViewModal) { initializeViewIncidentsButton(getCurrentEventData, openIncidentsViewModal); } } }