Crvi/assets/js/modules/agenda-event-mapper.js

442 lines
21 KiB
JavaScript

// Module de mapping des événements pour FullCalendar
// Contient toute la logique de transformation des données API vers le format FullCalendar
/**
* Calcule la luminosité d'une couleur hexadécimale
* @param {string} hexColor - Couleur au format #RRGGBB
* @returns {number} - Luminosité entre 0 et 1
*/
function getLuminance(hexColor) {
// Vérifier que hexColor n'est pas null ou undefined
if (!hexColor || typeof hexColor !== 'string') {
console.warn('⚠️ [getLuminance] Valeur hexColor invalide:', hexColor);
return 0.5; // Retourner une valeur moyenne par défaut
}
// Convertir hex en RGB
const hex = hexColor.replace('#', '');
const r = parseInt(hex.substr(0, 2), 16);
const g = parseInt(hex.substr(2, 2), 16);
const b = parseInt(hex.substr(4, 2), 16);
// Calculer la luminosité relative selon WCAG
return (0.299 * r + 0.587 * g + 0.114 * b) / 255;
}
/**
* Détermine la couleur de texte optimale selon le contraste
* @param {string} backgroundColor - Couleur de fond au format #RRGGBB
* @returns {string} - Couleur de texte (#000000 ou #ffffff)
*/
function getTextColor(backgroundColor) {
// Vérifier que backgroundColor n'est pas null ou undefined
if (!backgroundColor || typeof backgroundColor !== 'string') {
console.warn('⚠️ [getTextColor] Valeur backgroundColor invalide:', backgroundColor);
return '#000000'; // Retourner noir par défaut
}
const luminance = getLuminance(backgroundColor);
return luminance > 0.5 ? '#000000' : '#ffffff';
}
/**
* Mappe un événement API vers le format FullCalendar
* @param {Object} ev - Événement depuis l'API
* @returns {Object} - Événement au format FullCalendar
*/
export function mapEventToFullCalendar(ev) {
// Hiérarchie de détermination des couleurs :
// 1) Pour RDV assignés (individuel/groupe) avec type d'intervention : couleur du type d'intervention
// 2) Pour RDV assignés (individuel/groupe) sans type d'intervention : orange (par défaut)
// 3) Pour permanences : couleur selon le type (assignée, non attribuée, non disponible)
let backgroundColor = null;
let textColor = null;
// Vérifier si l'événement est assigné (a un intervenant et un local/beneficiaire)
const isEventAssigned = ev.id_intervenant && (ev.id_local || ev.id_beneficiaire);
// Pour les RDV (individuel/groupe) assignés
if (ev.type && ev.type !== 'permanence' && isEventAssigned) {
// Vérifier si l'événement a un type d'intervention défini
let typeInterventionId = null;
if (ev.id_type_intervention) {
typeInterventionId = parseInt(ev.id_type_intervention);
} else if (ev.type_intervention && ev.type_intervention.id) {
typeInterventionId = parseInt(ev.type_intervention.id);
}
// Si l'événement a un type d'intervention, utiliser sa couleur depuis crviAjax
if (typeInterventionId && !isNaN(typeInterventionId) && window.crviAjax && window.crviAjax.couleurs_types_intervention) {
const couleurTypeIntervention = window.crviAjax.couleurs_types_intervention[typeInterventionId];
if (couleurTypeIntervention) {
backgroundColor = couleurTypeIntervention;
textColor = getTextColor(couleurTypeIntervention);
console.log('🎨 [COULEUR] RDV assigné avec type d\'intervention:', {
eventId: ev.id,
type: ev.type,
typeInterventionId: typeInterventionId,
backgroundColor: backgroundColor,
textColor: textColor,
source: 'type_intervention'
});
}
}
// Si pas de couleur définie (pas de type d'intervention), garder orange par défaut
if (!backgroundColor) {
backgroundColor = '#ff9800'; // Orange pour les événements assignés sans type d'intervention
textColor = getTextColor(backgroundColor);
console.log('🎨 [COULEUR] RDV assigné sans type d\'intervention (défaut orange):', {
eventId: ev.id,
type: ev.type,
backgroundColor: backgroundColor,
textColor: textColor,
source: 'defaut_assigné'
});
}
}
// Pour les RDV non assignés : couleur selon type_de_local (bureau ou salle)
if (!backgroundColor && ev.type && ev.type !== 'permanence' && !isEventAssigned) {
// Vérifier si l'événement a un local avec un type_de_local
if (ev.local && ev.local.type_de_local) {
const typeLocal = ev.local.type_de_local.toLowerCase();
if (window.crviACFData && window.crviACFData.couleurs_rdv) {
const couleurRdv = window.crviACFData.couleurs_rdv[typeLocal];
if (couleurRdv) {
backgroundColor = couleurRdv;
textColor = getTextColor(couleurRdv);
console.log('🎨 [COULEUR] RDV non assigné selon type de local:', {
eventId: ev.id,
type: ev.type,
typeLocal: typeLocal,
backgroundColor: backgroundColor,
textColor: textColor,
source: 'type_local'
});
}
}
}
}
// Pour permanences (événements de type 'permanence') : logique simplifiée
// 1) Non attribuée : dispo ? couleur dispo : couleur indispo
// 2) Attribuée : a type intervention ? couleur type : couleur défaut
let isPermanenceDisabled = false;
if (!backgroundColor && ev.type === 'permanence') {
// Une permanence est assignée si elle a un bénéficiaire ET un local (assign = 1)
const isPermanenceAssigned = ev.assign === 1 || (ev.id_beneficiaire && ev.id_local);
// Une permanence est non attribuée si elle n'est pas assignée (assign = 0)
const isPermanenceNonAttribuee = ev.assign === 0 && !ev.id_beneficiaire && !ev.id_local;
// Vérifier si le bénéficiaire est en congé (indisponibilitee_ponctuelle)
let isPermanenceNonDisponible = false;
if (ev.beneficiaire && ev.beneficiaire.indisponibilitee_ponctuelle && Array.isArray(ev.beneficiaire.indisponibilitee_ponctuelle)) {
const eventDate = ev.date_rdv; // Format YYYY-MM-DD
const eventDateObj = new Date(eventDate + 'T00:00:00');
// Vérifier si la date de l'événement est dans une période d'indisponibilité
for (const indispo of ev.beneficiaire.indisponibilitee_ponctuelle) {
if (indispo.debut && indispo.fin) {
let debutDate, finDate;
// Gérer le format d/m/Y (format ACF)
if (typeof indispo.debut === 'string' && indispo.debut.includes('/')) {
const debutParts = indispo.debut.split('/');
const finParts = indispo.fin.split('/');
if (debutParts.length === 3 && finParts.length === 3) {
// Format d/m/Y
debutDate = new Date(parseInt(debutParts[2]), parseInt(debutParts[1]) - 1, parseInt(debutParts[0]));
finDate = new Date(parseInt(finParts[2]), parseInt(finParts[1]) - 1, parseInt(finParts[0]));
}
} else {
// Format YYYY-MM-DD ou timestamp
debutDate = new Date(indispo.debut);
finDate = new Date(indispo.fin);
}
if (debutDate && finDate && !isNaN(debutDate.getTime()) && !isNaN(finDate.getTime())) {
// Ajuster pour inclure toute la journée
debutDate.setHours(0, 0, 0, 0);
finDate.setHours(23, 59, 59, 999);
if (eventDateObj >= debutDate && eventDateObj <= finDate) {
isPermanenceNonDisponible = true;
isPermanenceDisabled = true;
break;
}
}
}
}
}
// Vérifier si l'intervenant est indisponible (congés ou jour non disponible)
if (!isPermanenceNonDisponible && ev.id_intervenant && window.crviACFData && window.crviACFData.indisponibilites_intervenants) {
const intervenantId = parseInt(ev.id_intervenant);
const intervenantDispo = window.crviACFData.indisponibilites_intervenants[intervenantId];
if (intervenantDispo) {
const eventDate = ev.date_rdv; // Format YYYY-MM-DD
const eventDateObj = new Date(eventDate + 'T00:00:00');
// Vérifier les jours de disponibilité (0 = dimanche, 1 = lundi, etc.)
const dayOfWeek = eventDateObj.getDay();
const dayNames = ['dimanche', 'lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi', 'samedi'];
const dayName = dayNames[dayOfWeek];
// Si l'intervenant a des jours de disponibilité définis et que ce jour n'en fait pas partie
if (intervenantDispo.jours_dispo && Array.isArray(intervenantDispo.jours_dispo) &&
intervenantDispo.jours_dispo.length > 0 &&
!intervenantDispo.jours_dispo.includes(dayName)) {
console.log('🚫 [PERMANENCE] Intervenant non disponible ce jour:', {
intervenantId,
date: eventDate,
dayName,
joursDisponibles: intervenantDispo.jours_dispo
});
isPermanenceNonDisponible = true;
isPermanenceDisabled = true;
}
// Vérifier les congés/indisponibilités ponctuelles
if (!isPermanenceNonDisponible && intervenantDispo.conges && Array.isArray(intervenantDispo.conges)) {
for (const conge of intervenantDispo.conges) {
if (conge.debut && conge.fin) {
let debutDate, finDate;
// Gérer le format d/m/Y (format ACF)
if (typeof conge.debut === 'string' && conge.debut.includes('/')) {
const debutParts = conge.debut.split('/');
const finParts = conge.fin.split('/');
if (debutParts.length === 3 && finParts.length === 3) {
debutDate = new Date(parseInt(debutParts[2]), parseInt(debutParts[1]) - 1, parseInt(debutParts[0]));
finDate = new Date(parseInt(finParts[2]), parseInt(finParts[1]) - 1, parseInt(finParts[0]));
}
} else {
// Format YYYY-MM-DD ou timestamp
debutDate = new Date(conge.debut);
finDate = new Date(conge.fin);
}
if (debutDate && finDate && !isNaN(debutDate.getTime()) && !isNaN(finDate.getTime())) {
debutDate.setHours(0, 0, 0, 0);
finDate.setHours(23, 59, 59, 999);
if (eventDateObj >= debutDate && eventDateObj <= finDate) {
console.log('🚫 [PERMANENCE] Intervenant en congé:', {
intervenantId,
date: eventDate,
congeDebut: conge.debut,
congeFin: conge.fin,
congeType: conge.type
});
isPermanenceNonDisponible = true;
isPermanenceDisabled = true;
break;
}
}
}
}
}
}
}
// Vérifier aussi le statut si pas déjà déterminé
if (!isPermanenceNonDisponible && ev.statut && ['annule', 'non_tenu', 'absence'].includes(ev.statut)) {
isPermanenceNonDisponible = true;
}
// LOGIQUE SIMPLIFIÉE
if (isPermanenceNonAttribuee) {
// 1) Non attribuée : dispo ? couleur dispo : couleur indispo
if (isPermanenceNonDisponible && window.crviAjax && window.crviAjax.couleur_permanence_non_disponible) {
backgroundColor = window.crviAjax.couleur_permanence_non_disponible;
console.log('🎨 [COULEUR] Permanence non attribuée - indisponible:', {
eventId: ev.id,
backgroundColor: backgroundColor,
source: 'permanence_non_attribuee_indispo'
});
} else if (window.crviAjax && window.crviAjax.couleur_permanence_non_attribuee) {
backgroundColor = window.crviAjax.couleur_permanence_non_attribuee;
console.log('🎨 [COULEUR] Permanence non attribuée - disponible:', {
eventId: ev.id,
backgroundColor: backgroundColor,
source: 'permanence_non_attribuee_dispo'
});
}
} else if (isPermanenceAssigned) {
// 2) Attribuée : a type intervention ? couleur type : couleur défaut
let typeInterventionId = null;
if (ev.id_type_intervention) {
typeInterventionId = parseInt(ev.id_type_intervention);
} else if (ev.type_intervention && ev.type_intervention.id) {
typeInterventionId = parseInt(ev.type_intervention.id);
}
if (typeInterventionId && !isNaN(typeInterventionId) && window.crviAjax && window.crviAjax.couleurs_types_intervention) {
const couleurTypeIntervention = window.crviAjax.couleurs_types_intervention[typeInterventionId];
if (couleurTypeIntervention) {
backgroundColor = couleurTypeIntervention;
console.log('🎨 [COULEUR] Permanence assignée - avec type intervention:', {
eventId: ev.id,
typeInterventionId: typeInterventionId,
backgroundColor: backgroundColor,
source: 'permanence_assignee_type'
});
}
}
// Si pas de type d'intervention, utiliser couleur défaut
if (!backgroundColor) {
backgroundColor = (window.crviACFData && window.crviACFData.couleurs_permanence && window.crviACFData.couleurs_permanence.permanence)
? window.crviACFData.couleurs_permanence.permanence
: '#9e9e9e';
console.log('🎨 [COULEUR] Permanence assignée - défaut:', {
eventId: ev.id,
backgroundColor: backgroundColor,
source: 'permanence_assignee_defaut'
});
}
}
// S'assurer que backgroundColor n'est pas null avant d'appeler getTextColor
if (backgroundColor) {
textColor = getTextColor(backgroundColor);
} else {
textColor = '#000000'; // Par défaut
}
}
// Couleur par défaut si aucune condition n'est remplie
if (!backgroundColor) {
backgroundColor = '#6c757d'; // Gris par défaut
textColor = getTextColor(backgroundColor);
console.log('🎨 [COULEUR] Couleur par défaut (gris):', {
eventId: ev.id,
type: ev.type,
backgroundColor: backgroundColor,
textColor: textColor,
source: 'defaut_general'
});
}
// Log final de la couleur appliquée
console.log('🎨 [COULEUR FINALE] Événement:', {
eventId: ev.id,
title: ev.type || 'Sans type',
backgroundColor: backgroundColor,
borderColor: backgroundColor,
textColor: textColor,
isAssigned: isEventAssigned,
type: ev.type
});
// Générer un titre plus explicite
let title = ev.type || 'Sans type';
// Gestion spéciale pour les permanences
if (ev.type === 'permanence') {
if (ev.intervenant) {
const intervenant = ev.intervenant;
const nomComplet = `${intervenant.prenom || ''} ${intervenant.nom || ''}`.trim();
title = 'p. ' + (nomComplet || 'Intervenant');
} else {
title = 'p. Intervenant';
}
} else {
// Utiliser le nom de l'intervenant comme titre principal pour les autres événements
if (ev.intervenant) {
const intervenant = ev.intervenant;
const nomComplet = `${intervenant.prenom || ''} ${intervenant.nom || ''}`.trim();
if (nomComplet) {
title = nomComplet;
}
}
// Ajouter le type d'intervention principal si disponible
if (ev.intervenant && ev.intervenant.types_intervention_noms && ev.intervenant.types_intervention_noms.length > 0) {
const primaryType = ev.intervenant.types_intervention_noms[0]; // Premier type d'intervention
title += ` - ${primaryType}`;
}
// Ajouter le commentaire si disponible
if (ev.commentaire) {
title += ' - ' + ev.commentaire;
}
}
// S'assurer que les couleurs sont toujours définies
if (!backgroundColor || typeof backgroundColor !== 'string') {
console.warn('⚠️ [Mapping] backgroundColor invalide pour l\'événement:', ev.id, backgroundColor);
backgroundColor = '#6c757d'; // Gris par défaut
}
if (!textColor || typeof textColor !== 'string') {
textColor = getTextColor(backgroundColor);
}
// Vérifier si l'événement est passé
const eventStartDate = new Date(ev.date_rdv + 'T' + ev.heure_rdv);
const now = new Date();
const isEventPast = eventStartDate < now;
// Un événement est éditable seulement si :
// 1. Il n'est pas désactivé (permanence)
// 2. Il n'est pas passé
const isEditable = !isPermanenceDisabled && !isEventPast;
return {
id: ev.id,
title: title,
start: ev.date_rdv + 'T' + ev.heure_rdv,
end: ev.date_fin + 'T' + ev.heure_fin,
backgroundColor: backgroundColor,
borderColor: backgroundColor,
textColor: textColor,
editable: isEditable, // Désactiver l'édition si la permanence est désactivée OU si l'événement est passé
durationEditable: isEditable, // Désactiver le redimensionnement si la permanence est désactivée OU si l'événement est passé
startEditable: isEditable, // Désactiver le déplacement si la permanence est désactivée OU si l'événement est passé
classNames: isPermanenceDisabled ? ['permanence-disabled'] : (isEventPast ? ['event-past'] : []),
extendedProps: {
// Données de base
type: ev.type,
commentaire: ev.commentaire,
date_rdv: ev.date_rdv,
heure_rdv: ev.heure_rdv,
date_fin: ev.date_fin,
heure_fin: ev.heure_fin,
// Relations avec les entités
id_beneficiaire: ev.id_beneficiaire,
id_intervenant: ev.id_intervenant,
id_traducteur: ev.id_traducteur,
id_local: ev.id_local,
id_departement: ev.id_departement,
id_type_intervention: ev.id_type_intervention || (ev.type_intervention && ev.type_intervention.id ? ev.type_intervention.id : null),
langue: ev.langue,
langues_disponibles: ev.langues_disponibles || null,
// Données des entités (si disponibles)
beneficiaire: ev.beneficiaire,
intervenant: ev.intervenant,
traducteur: ev.traducteur,
local: ev.local,
// Données spécifiques aux groupes
nb_participants: ev.nb_participants,
nb_hommes: ev.nb_hommes,
nb_femmes: ev.nb_femmes,
// Autres données
statut: ev.statut,
assign: ev.assign || 0,
isDisabled: isPermanenceDisabled,
created_at: ev.created_at,
updated_at: ev.updated_at
}
};
}