1001 lines
41 KiB
JavaScript
1001 lines
41 KiB
JavaScript
/**
|
||
* 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 = `
|
||
<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();
|
||
}
|
||
}
|
||
|