diff --git a/assets/js/modules/agenda-modal-buttons.js b/assets/js/modules/agenda-modal-buttons.js index 9f6d700..2097945 100644 --- a/assets/js/modules/agenda-modal-buttons.js +++ b/assets/js/modules/agenda-modal-buttons.js @@ -3,6 +3,9 @@ import { deleteEvent, changeEventStatus } 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 @@ -239,18 +242,332 @@ export function initializeCancelAppointmentButton(getCurrentEventData, onCancell }; } +/** + * 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); + + // 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); + + // 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 || ''; + } + } + ); + }; +} + +/** + * 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 (la fonction doit être importée depuis agenda-modal.js) + // Pour l'instant on laisse vide, le chargement sera fait dans le modal principal + }, + (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; @@ -261,9 +578,24 @@ export function initializeModalButtons(options = {}) { // 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); + } } } diff --git a/assets/js/modules/agenda-modal-display.js b/assets/js/modules/agenda-modal-display.js index fe77737..e1945dc 100644 --- a/assets/js/modules/agenda-modal-display.js +++ b/assets/js/modules/agenda-modal-display.js @@ -2,6 +2,7 @@ // Contient les fonctions d'affichage en mode lecture seule import { setText, toggleElement } from './agenda-modal-dom.js'; +import { checkAndDisplayIncidentsButton } from './agenda-modal.js'; /** * Extrait la date et l'heure d'un événement selon différentes sources @@ -208,6 +209,12 @@ export function fillViewBlock(event) { historiqueBtn.removeAttribute('data-benef'); } } + + // Vérifier et afficher le bouton "Détail incident(s)" si l'événement a des incidents + const eventId = event?.id || event?.extendedProps?.id; + if (eventId) { + checkAndDisplayIncidentsButton(eventId); + } } /** diff --git a/assets/js/modules/agenda-modal.js b/assets/js/modules/agenda-modal.js index d725464..77b9dee 100644 --- a/assets/js/modules/agenda-modal.js +++ b/assets/js/modules/agenda-modal.js @@ -104,6 +104,128 @@ export function openSubModal(subModalId, onBeforeOpen = null, onAfterClose = nul }, delay); } +/** + * Charge les incidents d'un événement + * @param {number} eventId - ID de l'événement + * @returns {Promise} - Tableau des incidents + */ +async function loadEventIncidents(eventId) { + try { + const incidents = await apiFetch(`events/${eventId}/incidents`); + return incidents || []; + } catch (error) { + console.error('Erreur lors du chargement des incidents:', error); + return []; + } +} + +/** + * Ouvre la modal d'incidents en mode affichage + * @param {number} eventId - ID de l'événement + */ +async function openIncidentsViewModal(eventId) { + const incidentModal = document.getElementById('declarationIncidentModal'); + 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 (!incidentModal || !viewSection || !formSection || !listContainer) { + notifyError('Modal d\'incidents introuvable'); + return; + } + + try { + // Charger les incidents + const incidents = await loadEventIncidents(eventId); + + if (incidents.length === 0) { + listContainer.innerHTML = '

Aucun incident signalé pour cet événement.

'; + } else { + // Afficher les incidents + let html = '
'; + incidents.forEach((incident, index) => { + const createdAt = incident.created_at ? new Date(incident.created_at).toLocaleDateString('fr-FR') : 'Date inconnue'; + html += ` +
+
+
Incident #${index + 1}
+ ${createdAt} +
+ ${incident.resume_incident ? `

Résumé : ${incident.resume_incident}

` : ''} + ${incident.commentaire_incident ? `

${incident.commentaire_incident}

` : ''} +
+ `; + }); + html += '
'; + listContainer.innerHTML = html; + } + + // Changer le titre de la modal + if (modalTitle) { + modalTitle.innerHTML = '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'; + + // Ouvrir la modal + const bsModal = new bootstrap.Modal(incidentModal); + bsModal.show(); + + // Réinitialiser au mode formulaire quand la modal se ferme + incidentModal.addEventListener('hidden.bs.modal', function resetModalMode() { + viewSection.style.display = 'none'; + formSection.style.display = 'block'; + viewFooter.style.display = 'none'; + formFooter.style.display = 'block'; + if (modalTitle) { + modalTitle.innerHTML = 'Signaler un incident'; + } + // Retirer l'écouteur pour éviter les duplications + incidentModal.removeEventListener('hidden.bs.modal', resetModalMode); + }); + + } catch (error) { + console.error('Erreur lors de l\'ouverture de la modal d\'incidents:', error); + notifyError('Erreur lors du chargement des incidents'); + } +} + +/** + * 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 : ''; + viewIncidentsBtn.innerHTML = `${iconHtml}Détail incident(s) (${incidents.length})`; + } else { + viewIncidentsBtn.style.display = 'none'; + } + } catch (error) { + console.error('Erreur lors de la vérification des incidents:', error); + viewIncidentsBtn.style.display = 'none'; + } +} + /** * Ouvre le modal d'événement * @param {string} mode - Mode ('view', 'edit', 'create') @@ -143,7 +265,13 @@ export function openModal(mode, eventData = null) { // 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() });