/** * Module Agenda Intervenant * Adapte l'agenda existant pour utiliser les endpoints intervenant * Réutilise les modules agenda-fullcalendar.js, agenda-filters.js, etc. */ import { apiFetch, updateEvent, getEvent } from './agenda-api.js'; import { notifyError, notifySuccess } from './agenda-notifications.js'; import { openModal } from './agenda-modal.js'; import { Calendar } from '@fullcalendar/core'; import dayGridPlugin from '@fullcalendar/daygrid'; import timeGridPlugin from '@fullcalendar/timegrid'; import listPlugin from '@fullcalendar/list'; import interactionPlugin from '@fullcalendar/interaction'; // Mode de vue : 'mine' ou 'colleagues' let currentViewMode = 'mine'; /** * Initialise le calendrier pour l'intervenant (mon agenda) */ export function initializeIntervenantCalendar() { console.log('🚀 Initialisation du calendrier intervenant (mon agenda)...'); const calendarEl = document.getElementById('agenda-calendar'); if (!calendarEl) { console.error('❌ Élément agenda-calendar non trouvé'); return null; } currentViewMode = 'mine'; const calendar = new Calendar(calendarEl, { plugins: [dayGridPlugin, timeGridPlugin, listPlugin, interactionPlugin], initialView: 'dayGridMonth', eventDisplay: 'block', locale: 'fr', firstDay: 1, headerToolbar: { left: 'prev,next today', center: 'title', right: 'dayGridMonth,timeGridWeek,timeGridDay,listWeek' }, buttonText: { today: 'Aujourd\'hui', month: 'Mois', week: 'Semaine', day: 'Jour', list: 'Liste' }, height: 'auto', // On active l'édition globalement, mais on contrôle au niveau de chaque // événement + des permissions (is_mine + crviPermissions.can_edit) editable: true, eventStartEditable: true, eventDurationEditable: true, eventResizableFromStart: true, selectable: true, // Activer la sélection pour créer des RDV dayMaxEvents: 3, weekends: true, moreLinkClick: 'popover', // Afficher les événements cachés dans un popover moreLinkContent: function(arg) { // Personnaliser le texte du lien "+x plus" const count = arg.num; return `+${count} plus`; }, // Gestionnaire de sélection de plage de dates pour créer un événement select: function(arg) { // Vérifier les permissions de création const userCanCreate = window.crviPermissions && window.crviPermissions.can_create; if (!userCanCreate) { notifyError('Vous n\'êtes pas autorisé à créer des événements'); console.warn('❌ Utilisateur non autorisé à créer des événements (permissions WP)'); return; } // Vérifier que la date de début n'est pas dans le passé if (arg.startStr) { const startDateStr = arg.startStr.split('T')[0]; const today = new Date(); today.setHours(0, 0, 0, 0); const selectedDate = new Date(startDateStr + 'T00:00:00'); selectedDate.setHours(0, 0, 0, 0); if (selectedDate < today) { notifyError('Impossible de créer un événement à une date passée'); console.warn('⚠️ Tentative de création d\'événement à une date passée:', startDateStr); return; } } // Préremplir avec l'intervenant actuel (utilisateur connecté) const currentUserId = window.crviPermissions && window.crviPermissions.user_id; // Ouvrir le modal de création avec l'intervenant prérempli const eventData = { ...arg, extendedProps: { id_intervenant: currentUserId } }; openModal('create', eventData); }, // Charger les événements depuis l'endpoint intervenant events: async function(fetchInfo, successCallback, failureCallback) { try { const filters = collectIntervenantFilters(); const params = { start: fetchInfo.startStr.split('T')[0], end: fetchInfo.endStr.split('T')[0], view_mode: 'mine', // Toujours en mode "mon agenda" filters: JSON.stringify(filters) }; const events = await apiFetch('intervenant/agenda?' + new URLSearchParams(params)); // Formater pour FullCalendar const formattedEvents = events.map(event => { // Extraire la date et l'heure depuis start si disponibles let date_rdv = null; let heure_rdv = null; if (event.start) { const startDate = new Date(event.start); date_rdv = startDate.toISOString().split('T')[0]; heure_rdv = startDate.toTimeString().substring(0, 5); } return { id: event.id, title: event.title, start: event.start, end: event.end, backgroundColor: getEventColor(event), borderColor: getEventColor(event), textColor: '#fff', // Seuls les événements "à moi" sont éditables côté UI editable: !!event.is_mine, durationEditable: !!event.is_mine, startEditable: !!event.is_mine, extendedProps: { type: event.type, statut: event.statut, is_mine: event.is_mine, show_comments: event.show_comments, commentaire: event.commentaire, beneficiaire: event.beneficiaire, local: event.local, intervenant_nom: event.intervenant_nom, date: date_rdv, date_rdv: date_rdv, heure: heure_rdv, heure_rdv: heure_rdv, langue: event.langue, langues_disponibles: event.langues_disponibles || null } }; }); successCallback(formattedEvents); } catch (error) { console.error('Erreur lors du chargement de l\'agenda:', error); failureCallback(error); } }, // Déplacement d'un événement (drag & drop) eventDrop: async function(info) { const props = info.event.extendedProps || {}; // Vérifier que l'événement appartient bien à l'intervenant if (!props.is_mine) { console.warn('❌ Tentative de déplacement d’un événement qui ne vous appartient pas'); info.revert(); notifyError('Vous ne pouvez déplacer que vos propres rendez-vous'); return; } // Vérifier les permissions globales const userCanEdit = window.crviPermissions && window.crviPermissions.can_edit; if (!userCanEdit) { console.warn('❌ Utilisateur non autorisé à déplacer des événements (permissions WP)'); info.revert(); notifyError('Vous n\'êtes pas autorisé à modifier les événements'); return; } try { const newStart = info.event.start; const newEnd = info.event.end || new Date(newStart.getTime() + 60 * 60 * 1000); // +1h par défaut const updateData = { date_rdv: newStart.toISOString().split('T')[0], heure_rdv: newStart.toTimeString().substring(0, 5), date_fin: newEnd.toISOString().split('T')[0], heure_fin: newEnd.toTimeString().substring(0, 5) }; await updateEvent(info.event.id, updateData); notifySuccess('Événement déplacé avec succès'); } catch (error) { console.error('Erreur lors du déplacement de l\'événement (front intervenant):', error); info.revert(); notifyError('Erreur lors du déplacement de l\'événement'); } }, // Redimensionnement d'un événement (changement de durée) eventResize: async function(info) { const props = info.event.extendedProps || {}; if (!props.is_mine) { console.warn('❌ Tentative de redimensionnement d’un événement qui ne vous appartient pas'); info.revert(); notifyError('Vous ne pouvez modifier que vos propres rendez-vous'); return; } const userCanEdit = window.crviPermissions && window.crviPermissions.can_edit; if (!userCanEdit) { console.warn('❌ Utilisateur non autorisé à redimensionner des événements (permissions WP)'); info.revert(); notifyError('Vous n\'êtes pas autorisé à modifier les événements'); return; } try { const newEnd = info.event.end; const updateData = { date_fin: newEnd.toISOString().split('T')[0], heure_fin: newEnd.toTimeString().substring(0, 5) }; await updateEvent(info.event.id, updateData); notifySuccess('Événement redimensionné avec succès'); } catch (error) { console.error('Erreur lors du redimensionnement de l\'événement (front intervenant):', error); info.revert(); notifyError('Erreur lors du redimensionnement de l\'événement'); } }, eventClick: function(info) { // Ouvrir le modal avec les détails (adapter le modal existant) openIntervenantEventModal(info.event); }, // Ajouter les popovers Bootstrap sur les événements eventDidMount: function(arg) { const eventEl = arg.el; const event = arg.event; const eventProps = event.extendedProps || {}; // Fonction utilitaire pour formater une date au format français function formatDateToFrench(dateString) { if (!dateString) return ''; try { const date = new Date(dateString); if (isNaN(date.getTime())) { // Si ce n'est pas une date valide, essayer le format YYYY-MM-DD const parts = dateString.split('-'); if (parts.length === 3) { return `${parts[2]}/${parts[1]}/${parts[0]}`; } return dateString; } return date.toLocaleDateString('fr-FR'); } catch (error) { console.error('Erreur lors du formatage de la date:', error); return dateString; } } // Fonction utilitaire pour extraire le nom complet d'une entité function getEntityDisplayName(entity, entityType = '') { if (!entity) return ''; // Si c'est déjà une chaîne, la retourner if (typeof entity === 'string') return entity; // Si c'est un objet avec une propriété nom if (entity && typeof entity === 'object') { // Pour les bénéficiaires et intervenants, combiner prénom et nom if (entityType === 'beneficiaire' || entityType === 'intervenant') { const prenom = entity.prenom || ''; const nom = entity.nom || ''; return `${prenom} ${nom}`.trim() || entity.nom || entity.display_name || ''; } // Pour les locaux, utiliser le nom if (entityType === 'local') { return entity.nom || entity.display_name || ''; } // Fallback générique return entity.nom || entity.display_name || entity.name || ''; } return ''; } // Extraire la date et l'heure depuis event.start si pas dans extendedProps let dateStr = eventProps.date || eventProps.date_rdv || ''; let heureStr = eventProps.heure || eventProps.heure_rdv || ''; if (!dateStr && event.start) { const startDate = new Date(event.start); dateStr = startDate.toISOString().split('T')[0]; } if (!heureStr && event.start) { const startDate = new Date(event.start); heureStr = startDate.toTimeString().substring(0, 5); } // Créer le contenu du popover const popoverContent = `
${event.title}
Date: ${formatDateToFrench(dateStr)}
Heure: ${heureStr}
Type: ${eventProps.type || ''}
${eventProps.langue ? `
Langue: ${eventProps.langue}
` : ''} ${eventProps.beneficiaire ? `
Bénéficiaire: ${typeof eventProps.beneficiaire === 'string' ? eventProps.beneficiaire : getEntityDisplayName(eventProps.beneficiaire, 'beneficiaire')}
` : ''} ${eventProps.intervenant_nom ? `
Intervenant: ${eventProps.intervenant_nom}
` : ''} ${eventProps.local ? `
Local: ${typeof eventProps.local === 'string' ? eventProps.local : getEntityDisplayName(eventProps.local, 'local')}
` : ''} ${eventProps.commentaire && eventProps.show_comments ? `
Commentaire: ${eventProps.commentaire}
` : ''}
Statut: ${eventProps.statut || ''}
Cliquez pour plus de détails
`; // Initialiser le popover Bootstrap if (window.bootstrap && window.bootstrap.Popover) { const popover = new bootstrap.Popover(eventEl, { title: 'Détails de l\'événement', content: popoverContent, html: true, trigger: 'hover', placement: 'top', container: 'body', template: '' }); } } }); calendar.render(); window.currentCalendar = calendar; // Masquer l'indicateur de chargement const loadingIndicator = document.getElementById('loading-indicator'); if (loadingIndicator) { loadingIndicator.style.display = 'none'; } // Initialiser les filtres adaptés pour intervenant initializeIntervenantFilters(calendar); return calendar; } /** * Initialise le calendrier pour les collègues */ export function initializeColleaguesCalendar() { console.log('🚀 Initialisation du calendrier collègues...'); const calendarEl = document.getElementById('agenda-calendar-colleagues'); if (!calendarEl) { console.error('❌ Élément agenda-calendar-colleagues non trouvé'); return null; } currentViewMode = 'colleagues'; const calendar = new Calendar(calendarEl, { plugins: [dayGridPlugin, timeGridPlugin, listPlugin, interactionPlugin], initialView: 'dayGridMonth', eventDisplay: 'block', locale: 'fr', firstDay: 1, headerToolbar: { left: 'prev,next today', center: 'title', right: 'dayGridMonth,timeGridWeek,timeGridDay,listWeek' }, buttonText: { today: 'Aujourd\'hui', month: 'Mois', week: 'Semaine', day: 'Jour', list: 'Liste' }, height: 'auto', editable: false, selectable: false, dayMaxEvents: 3, weekends: true, moreLinkClick: 'popover', // Afficher les événements cachés dans un popover moreLinkContent: function(arg) { // Personnaliser le texte du lien "+x plus" const count = arg.num; return `+${count} plus`; }, events: async function(fetchInfo, successCallback, failureCallback) { try { const filters = collectColleaguesFilters(); const params = { start: fetchInfo.startStr.split('T')[0], end: fetchInfo.endStr.split('T')[0], view_mode: 'colleagues', filters: JSON.stringify(filters) }; const events = await apiFetch('intervenant/agenda?' + new URLSearchParams(params)); const formattedEvents = events.map(event => { // Extraire la date et l'heure depuis start si disponibles let date_rdv = null; let heure_rdv = null; if (event.start) { const startDate = new Date(event.start); date_rdv = startDate.toISOString().split('T')[0]; heure_rdv = startDate.toTimeString().substring(0, 5); } return { id: event.id, title: event.title, start: event.start, end: event.end, backgroundColor: getEventColor(event), borderColor: getEventColor(event), textColor: '#fff', extendedProps: { type: event.type, statut: event.statut, is_mine: event.is_mine, show_comments: false, // Jamais de commentaires pour les collègues beneficiaire: event.beneficiaire, local: event.local, intervenant_nom: event.intervenant_nom, date: date_rdv, date_rdv: date_rdv, heure: heure_rdv, heure_rdv: heure_rdv, langue: event.langue } }; }); successCallback(formattedEvents); } catch (error) { console.error('Erreur lors du chargement de l\'agenda des collègues:', error); failureCallback(error); } }, eventClick: function(info) { openIntervenantEventModal(info.event); }, // Ajouter les popovers Bootstrap sur les événements eventDidMount: function(arg) { const eventEl = arg.el; const event = arg.event; const eventProps = event.extendedProps || {}; // Fonction utilitaire pour formater une date au format français function formatDateToFrench(dateString) { if (!dateString) return ''; try { const date = new Date(dateString); if (isNaN(date.getTime())) { // Si ce n'est pas une date valide, essayer le format YYYY-MM-DD const parts = dateString.split('-'); if (parts.length === 3) { return `${parts[2]}/${parts[1]}/${parts[0]}`; } return dateString; } return date.toLocaleDateString('fr-FR'); } catch (error) { console.error('Erreur lors du formatage de la date:', error); return dateString; } } // Fonction utilitaire pour extraire le nom complet d'une entité function getEntityDisplayName(entity, entityType = '') { if (!entity) return ''; // Si c'est déjà une chaîne, la retourner if (typeof entity === 'string') return entity; // Si c'est un objet avec une propriété nom if (entity && typeof entity === 'object') { // Pour les bénéficiaires et intervenants, combiner prénom et nom if (entityType === 'beneficiaire' || entityType === 'intervenant') { const prenom = entity.prenom || ''; const nom = entity.nom || ''; return `${prenom} ${nom}`.trim() || entity.nom || entity.display_name || ''; } // Pour les locaux, utiliser le nom if (entityType === 'local') { return entity.nom || entity.display_name || ''; } // Fallback générique return entity.nom || entity.display_name || entity.name || ''; } return ''; } // Extraire la date et l'heure depuis event.start si pas dans extendedProps let dateStr = eventProps.date || eventProps.date_rdv || ''; let heureStr = eventProps.heure || eventProps.heure_rdv || ''; if (!dateStr && event.start) { const startDate = new Date(event.start); dateStr = startDate.toISOString().split('T')[0]; } if (!heureStr && event.start) { const startDate = new Date(event.start); heureStr = startDate.toTimeString().substring(0, 5); } // Créer le contenu du popover const popoverContent = `
${event.title}
Date: ${formatDateToFrench(dateStr)}
Heure: ${heureStr}
Type: ${eventProps.type || ''}
${eventProps.langue ? `
Langue: ${eventProps.langue}
` : ''} ${eventProps.beneficiaire ? `
Bénéficiaire: ${typeof eventProps.beneficiaire === 'string' ? eventProps.beneficiaire : getEntityDisplayName(eventProps.beneficiaire, 'beneficiaire')}
` : ''} ${eventProps.intervenant_nom ? `
Intervenant: ${eventProps.intervenant_nom}
` : ''} ${eventProps.local ? `
Local: ${typeof eventProps.local === 'string' ? eventProps.local : getEntityDisplayName(eventProps.local, 'local')}
` : ''} ${eventProps.commentaire && eventProps.show_comments ? `
Commentaire: ${eventProps.commentaire}
` : ''}
Statut: ${eventProps.statut || ''}
Cliquez pour plus de détails
`; // Initialiser le popover Bootstrap if (window.bootstrap && window.bootstrap.Popover) { const popover = new bootstrap.Popover(eventEl, { title: 'Détails de l\'événement', content: popoverContent, html: true, trigger: 'hover', placement: 'top', container: 'body', template: '' }); } } }); calendar.render(); window.currentColleaguesCalendar = calendar; const loadingIndicator = document.getElementById('loading-indicator-colleagues'); if (loadingIndicator) { loadingIndicator.style.display = 'none'; } initializeColleaguesFilters(calendar); return calendar; } /** * Collecte les filtres depuis le formulaire "Mon agenda" */ function collectIntervenantFilters() { const filters = {}; const local = document.getElementById('local')?.value; if (local) filters.local_id = parseInt(local); const beneficiaire = document.getElementById('beneficiaire')?.value; if (beneficiaire) filters.beneficiaire_id = parseInt(beneficiaire); const typeRdv = document.getElementById('type_rdv')?.value; if (typeRdv) filters.type_rdv = typeRdv; const departement = document.getElementById('departement')?.value; if (departement) filters.departement = parseInt(departement); const typeIntervention = document.getElementById('type_intervention')?.value; if (typeIntervention) filters.type_intervention = parseInt(typeIntervention); const langue = document.getElementById('langue_filtre')?.value; if (langue) filters.langue = langue; const permanences = document.getElementById('permanences_non_assignees')?.checked; if (permanences) filters.permanences_non_assignees = true; return filters; } /** * Collecte les filtres depuis le formulaire "Agenda des collègues" */ function collectColleaguesFilters() { const filters = {}; const local = document.getElementById('local-colleagues')?.value; if (local) filters.local_id = parseInt(local); const beneficiaire = document.getElementById('beneficiaire-colleagues')?.value; if (beneficiaire) filters.beneficiaire_id = parseInt(beneficiaire); const typeRdv = document.getElementById('type_rdv-colleagues')?.value; if (typeRdv) filters.type_rdv = typeRdv; const departement = document.getElementById('departement-colleagues')?.value; if (departement) filters.departement = parseInt(departement); const typeIntervention = document.getElementById('type_intervention-colleagues')?.value; if (typeIntervention) filters.type_intervention = parseInt(typeIntervention); const langue = document.getElementById('langue-colleagues')?.value; if (langue) filters.langue = langue; return filters; } /** * Initialise les filtres pour "Mon agenda" */ function initializeIntervenantFilters(calendar) { // Charger les options de filtres depuis l'endpoint disponibilités loadFilterOptions('intervenant'); // Écouter les changements de filtres const filterForm = document.querySelector('.filters'); if (filterForm) { const filterBtn = document.getElementById('filterBtn'); if (filterBtn) { filterBtn.addEventListener('click', () => { calendar.refetchEvents(); }); } const resetBtn = document.getElementById('resetFiltersBtn'); if (resetBtn) { resetBtn.addEventListener('click', () => { filterForm.reset(); calendar.refetchEvents(); }); } // Bouton "Ajouter un événement" const addEventBtn = document.getElementById('addEventBtn'); if (addEventBtn) { addEventBtn.addEventListener('click', () => { // Vérifier les permissions de création const userCanCreate = window.crviPermissions && window.crviPermissions.can_create; if (!userCanCreate) { notifyError('Vous n\'êtes pas autorisé à créer des événements'); console.warn('❌ Utilisateur non autorisé à créer des événements (permissions WP)'); return; } // Ouvrir le modal de création avec la date du jour et l'intervenant prérempli const today = new Date(); const dateStr = today.toISOString().split('T')[0]; const startStr = dateStr + 'T09:00:00'; const endStr = dateStr + 'T10:00:00'; // Préremplir avec l'intervenant actuel (utilisateur connecté) const currentUserId = window.crviPermissions && window.crviPermissions.user_id; openModal('create', { start: new Date(startStr), end: new Date(endStr), startStr: startStr, endStr: endStr, allDay: false, extendedProps: { id_intervenant: currentUserId } }); }); } // Auto-filtrer sur changement const selects = filterForm.querySelectorAll('select, input[type="checkbox"]'); selects.forEach(el => { el.addEventListener('change', () => { setTimeout(() => calendar.refetchEvents(), 300); }); }); } } /** * Initialise les filtres pour "Agenda des collègues" */ function initializeColleaguesFilters(calendar) { loadFilterOptions('colleagues'); const filterForm = document.querySelector('.filters-colleagues'); if (filterForm) { const filterBtn = document.getElementById('filterBtn-colleagues'); if (filterBtn) { filterBtn.addEventListener('click', () => { calendar.refetchEvents(); }); } const resetBtn = document.getElementById('resetFiltersBtn-colleagues'); if (resetBtn) { resetBtn.addEventListener('click', () => { filterForm.reset(); calendar.refetchEvents(); }); } const selects = filterForm.querySelectorAll('select'); selects.forEach(el => { el.addEventListener('change', () => { setTimeout(() => calendar.refetchEvents(), 300); }); }); } } /** * Charge les options des filtres depuis l'endpoint disponibilités */ async function loadFilterOptions(mode = 'intervenant') { try { const disponibilites = await apiFetch('agenda/disponibilites'); const prefix = mode === 'colleagues' ? '-colleagues' : ''; // Locaux const localSelect = document.getElementById(`local${prefix}`); if (localSelect && disponibilites.locaux) { disponibilites.locaux.forEach(local => { const option = document.createElement('option'); option.value = local.id; option.textContent = local.nom; localSelect.appendChild(option); }); } // Bénéficiaires const beneficiaireSelect = document.getElementById(`beneficiaire${prefix}`); if (beneficiaireSelect && disponibilites.beneficiaires) { disponibilites.beneficiaires.forEach(benef => { const option = document.createElement('option'); option.value = benef.id; option.textContent = benef.nom; beneficiaireSelect.appendChild(option); }); } // Départements const departementSelect = document.getElementById(`departement${prefix}`); if (departementSelect && disponibilites.departements) { disponibilites.departements.forEach(dept => { const option = document.createElement('option'); option.value = dept.id; option.textContent = dept.nom; departementSelect.appendChild(option); }); } // Types d'intervention const typeInterventionSelect = document.getElementById(`type_intervention${prefix}`); if (typeInterventionSelect && disponibilites.types_intervention) { disponibilites.types_intervention.forEach(type => { const option = document.createElement('option'); option.value = type.id; option.textContent = type.nom; typeInterventionSelect.appendChild(option); }); } // Langues const langueSelect = document.getElementById(`langue${prefix}`); if (langueSelect && disponibilites.langues) { disponibilites.langues.forEach(langue => { const option = document.createElement('option'); option.value = langue.id; option.textContent = langue.nom; langueSelect.appendChild(option); }); } // Initialiser Select2 if (window.jQuery && jQuery().select2) { jQuery(`select${prefix ? '[id$="-colleagues"]' : ':not([id$="-colleagues"])'}`).select2(); } } catch (error) { console.error('Erreur lors du chargement des options de filtres:', error); } } /** * Détermine la couleur d'un événement selon son type/statut */ function getEventColor(event) { if (event.type === 'permanence') { return '#17a2b8'; // Teal pour les permanences } // Couleur selon le statut switch (event.statut) { case 'prevu': return '#28a745'; // Vert case 'present': return '#0d6efd'; // Bleu case 'absent': return '#dc3545'; // Rouge case 'cloture': return '#6c757d'; // Gris default: return '#ffc107'; // Jaune } } /** * Ouvre le modal complet (eventModal) en mode vue, comme côté admin. * Charge d'abord les détails complets de l'événement via l'API. */ async function openIntervenantEventModal(event) { const userCanView = window.crviPermissions && window.crviPermissions.can_view; if (!userCanView) { console.warn('❌ Utilisateur non autorisé à voir les événements (permissions WP)'); notifyError('Vous n\'êtes pas autorisé à consulter les événements'); return; } try { // Overlay sur le calendrier pendant le chargement des détails const calendarEl = document.getElementById('agenda-calendar') || document.getElementById('agenda-calendar-colleagues'); if (window.CRVI_OVERLAY && calendarEl) { window.CRVI_OVERLAY.show(calendarEl); } const eventId = event.id; const eventDetails = await getEvent(eventId); let startDate; let endDate; try { // Date de début if (eventDetails.date_rdv && eventDetails.heure_rdv) { startDate = new Date(eventDetails.date_rdv + 'T' + eventDetails.heure_rdv); if (isNaN(startDate.getTime())) { startDate = new Date(); } } else { startDate = new Date(event.start); } // Date de fin if (eventDetails.date_fin && eventDetails.heure_fin) { endDate = new Date(eventDetails.date_fin + 'T' + eventDetails.heure_fin); if (isNaN(endDate.getTime())) { endDate = startDate; } } else { endDate = event.end ? new Date(event.end) : startDate; } } catch (e) { console.error('Erreur lors de la création des dates (intervenant):', e); startDate = new Date(); endDate = new Date(); } const fullEvent = { id: eventDetails.id, start: startDate, end: endDate, extendedProps: { // Données de base type: eventDetails.type, commentaire: eventDetails.commentaire, date: eventDetails.date_rdv, heure: eventDetails.heure_rdv, date_fin: eventDetails.date_fin, heure_fin: eventDetails.heure_fin, // Relations id_beneficiaire: eventDetails.id_beneficiaire, id_intervenant: eventDetails.id_intervenant, id_traducteur: eventDetails.id_traducteur, id_local: eventDetails.id_local, langue: eventDetails.langue, langues_disponibles: eventDetails.langues_disponibles || null, beneficiaire: eventDetails.beneficiaire, intervenant: eventDetails.intervenant, traducteur: eventDetails.traducteur, local: eventDetails.local, // Données groupe nb_participants: eventDetails.nb_participants, nb_hommes: eventDetails.nb_hommes, nb_femmes: eventDetails.nb_femmes, // Autres statut: eventDetails.statut, created_at: eventDetails.created_at, updated_at: eventDetails.updated_at } }; // Ouvrir le modal complet en mode "view" (même pattern qu'en admin) openModal('view', fullEvent); } catch (error) { console.error('Erreur lors du chargement des détails de l\'événement (intervenant):', error); notifyError('Erreur lors du chargement des détails de l\'événement'); } finally { const calendarEl = document.getElementById('agenda-calendar') || document.getElementById('agenda-calendar-colleagues'); if (window.CRVI_OVERLAY && calendarEl) { window.CRVI_OVERLAY.hide(calendarEl); } } } /** * Retourne la classe Bootstrap pour le badge de statut */ function getStatusBadgeClass(statut) { switch (statut) { case 'prevu': return 'success'; case 'present': return 'primary'; case 'absent': return 'danger'; case 'cloture': return 'secondary'; case 'annule': return 'warning'; default: return 'secondary'; } } /** * Initialise les onglets pour basculer entre mon agenda et agenda des collègues */ export function initializeIntervenantAgendaTabs() { const myAgendaTab = document.getElementById('my-agenda-tab'); const colleaguesTab = document.getElementById('colleagues-agenda-tab'); let myCalendar = null; let colleaguesCalendar = null; if (myAgendaTab) { myAgendaTab.addEventListener('shown.bs.tab', () => { console.log('🔄 Basculement vers Mon Agenda (mode: mine)'); if (!myCalendar) { myCalendar = initializeIntervenantCalendar(); } else { myCalendar.refetchEvents(); } }); } if (colleaguesTab) { colleaguesTab.addEventListener('shown.bs.tab', () => { console.log('🔄 Basculement vers Agenda des collègues (mode: colleagues)'); if (!colleaguesCalendar) { colleaguesCalendar = initializeColleaguesCalendar(); } else { colleaguesCalendar.refetchEvents(); } }); } // Initialiser le premier onglet (mon agenda) par défaut if (myAgendaTab && myAgendaTab.classList.contains('active')) { myCalendar = initializeIntervenantCalendar(); } }