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 = `
+
+ `;
+ }
+
+ // 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()
});