Crvi/assets/js/modules/agenda-intervenant-calendar.js
2026-01-22 09:38:29 +01:00

1001 lines
41 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* 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 dun é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 dun é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 = `
<div class="event-popover">
<h6 class="mb-2">${event.title}</h6>
<div class="mb-1"><strong>Date:</strong> ${formatDateToFrench(dateStr)}</div>
<div class="mb-1"><strong>Heure:</strong> ${heureStr}</div>
<div class="mb-1"><strong>Type:</strong> ${eventProps.type || ''}</div>
${eventProps.langue ? `<div class="mb-1"><strong>Langue:</strong> ${eventProps.langue}</div>` : ''}
${eventProps.beneficiaire ? `<div class="mb-1"><strong>Bénéficiaire:</strong> ${typeof eventProps.beneficiaire === 'string' ? eventProps.beneficiaire : getEntityDisplayName(eventProps.beneficiaire, 'beneficiaire')}</div>` : ''}
${eventProps.intervenant_nom ? `<div class="mb-1"><strong>Intervenant:</strong> ${eventProps.intervenant_nom}</div>` : ''}
${eventProps.local ? `<div class="mb-1"><strong>Local:</strong> ${typeof eventProps.local === 'string' ? eventProps.local : getEntityDisplayName(eventProps.local, 'local')}</div>` : ''}
${eventProps.commentaire && eventProps.show_comments ? `<div class="mb-1"><strong>Commentaire:</strong> ${eventProps.commentaire}</div>` : ''}
<div class="mb-1"><strong>Statut:</strong> ${eventProps.statut || ''}</div>
<div class="mt-2">
<small class="text-muted">Cliquez pour plus de détails</small>
</div>
</div>
`;
// 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: '<div class="popover event-popover" role="tooltip"><div class="popover-arrow"></div><h3 class="popover-header"></h3><div class="popover-body"></div></div>'
});
}
}
});
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 = `
<div class="event-popover">
<h6 class="mb-2">${event.title}</h6>
<div class="mb-1"><strong>Date:</strong> ${formatDateToFrench(dateStr)}</div>
<div class="mb-1"><strong>Heure:</strong> ${heureStr}</div>
<div class="mb-1"><strong>Type:</strong> ${eventProps.type || ''}</div>
${eventProps.langue ? `<div class="mb-1"><strong>Langue:</strong> ${eventProps.langue}</div>` : ''}
${eventProps.beneficiaire ? `<div class="mb-1"><strong>Bénéficiaire:</strong> ${typeof eventProps.beneficiaire === 'string' ? eventProps.beneficiaire : getEntityDisplayName(eventProps.beneficiaire, 'beneficiaire')}</div>` : ''}
${eventProps.intervenant_nom ? `<div class="mb-1"><strong>Intervenant:</strong> ${eventProps.intervenant_nom}</div>` : ''}
${eventProps.local ? `<div class="mb-1"><strong>Local:</strong> ${typeof eventProps.local === 'string' ? eventProps.local : getEntityDisplayName(eventProps.local, 'local')}</div>` : ''}
${eventProps.commentaire && eventProps.show_comments ? `<div class="mb-1"><strong>Commentaire:</strong> ${eventProps.commentaire}</div>` : ''}
<div class="mb-1"><strong>Statut:</strong> ${eventProps.statut || ''}</div>
<div class="mt-2">
<small class="text-muted">Cliquez pour plus de détails</small>
</div>
</div>
`;
// 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: '<div class="popover event-popover" role="tooltip"><div class="popover-arrow"></div><h3 class="popover-header"></h3><div class="popover-body"></div></div>'
});
}
}
});
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 groupés par département
const typeInterventionSelect = document.getElementById(`type_intervention${prefix}`);
if (typeInterventionSelect && disponibilites.types_intervention_groupes) {
// Parcourir les départements et créer des optgroup
Object.keys(disponibilites.types_intervention_groupes).forEach(departementId => {
const departementData = disponibilites.types_intervention_groupes[departementId];
const optgroup = document.createElement('optgroup');
optgroup.label = departementData.nom;
// Ajouter les types d'intervention de ce département
if (departementData.types && Array.isArray(departementData.types)) {
departementData.types.forEach(type => {
const option = document.createElement('option');
option.value = type.id;
option.textContent = type.nom;
optgroup.appendChild(option);
});
}
typeInterventionSelect.appendChild(optgroup);
});
}
// 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();
}
}