Crvi/assets/js/modules/agenda-intervenant-profile.js
2026-01-20 07:54:37 +01:00

609 lines
22 KiB
JavaScript

/**
* Module Profil Intervenant
* Gestion du formulaire de profil et mise à jour des informations
*/
import { apiFetch } from './agenda-api.js';
import toastr from 'toastr';
// Variables globales pour gérer les indisponibilités
let indisponibilitesList = [];
let indisponibiliteCounter = 0;
/**
* Initialise le module profil intervenant
*/
export function initializeProfile() {
console.log('🚀 Initialisation du module profil intervenant...');
// Charger les données du profil
loadProfile();
// Écouter les événements du formulaire principal
const profileForm = document.getElementById('profile-form');
if (profileForm) {
profileForm.addEventListener('submit', handleProfileSubmit);
}
const cancelBtn = document.getElementById('cancel-profile-btn');
if (cancelBtn) {
cancelBtn.addEventListener('click', () => {
loadProfile(); // Recharger pour annuler les modifications
});
}
// Écouter les événements du formulaire de disponibilités
const disponibilitesForm = document.getElementById('disponibilites-form');
if (disponibilitesForm) {
disponibilitesForm.addEventListener('submit', handleDisponibilitesSubmit);
}
// Gestion de l'ajout d'indisponibilité
const addIndispoBtn = document.getElementById('add-indisponibilite-btn');
if (addIndispoBtn) {
addIndispoBtn.addEventListener('click', showIndisponibiliteForm);
}
const saveIndispoBtn = document.getElementById('save-indisponibilite-btn');
if (saveIndispoBtn) {
saveIndispoBtn.addEventListener('click', saveIndisponibilite);
}
const cancelIndispoBtn = document.getElementById('cancel-indisponibilite-btn');
if (cancelIndispoBtn) {
cancelIndispoBtn.addEventListener('click', hideIndisponibiliteForm);
}
}
/**
* Charge les données du profil depuis l'API
*/
async function loadProfile() {
try {
const profileContainer = document.getElementById('intervenant-profile-container') || document.getElementById('profile-form') || document.body;
if (window.CRVI_OVERLAY && profileContainer) { window.CRVI_OVERLAY.show(profileContainer); }
const profile = await apiFetch('intervenant/profile');
// Remplir les champs non modifiables
const nomEl = document.getElementById('profile-nom');
const prenomEl = document.getElementById('profile-prenom');
const emailEl = document.getElementById('profile-email');
if (nomEl) nomEl.value = profile.nom || '';
if (prenomEl) prenomEl.value = profile.prenom || '';
if (emailEl) emailEl.value = profile.email || '';
// Téléphone (modifiable)
const telephoneEl = document.getElementById('profile-telephone');
if (telephoneEl) {
telephoneEl.value = profile.telephone || '';
}
// Jours de disponibilité (checkboxes) - déjà générés en PHP, on met juste à jour les valeurs
updateJoursDisponibiliteCheckboxes(profile.jours_de_disponibilite || []);
// Heures de permanences (checkboxes)
updateHeuresPermanencesCheckboxes(profile.heures_de_permanences || []);
// Indisponibilités ponctuelles - déjà générées en PHP, on synchronise juste la liste
indisponibilitesList = profile.indisponibilites_ponctuelles || [];
// Synchroniser la liste avec celle affichée en PHP (pour les mises à jour après sauvegarde)
syncIndisponibilitesList(indisponibilitesList);
// Rafraîchir explicitement l'affichage pour éviter tout écart visuel
displayIndisponibilites(indisponibilitesList);
// Départements (format: [{id, nom}, ...])
displayDepartements(profile.departements || []);
// Spécialisations (format: [{id, nom}, ...])
displaySpecialisations(profile.specialisations || []);
} catch (error) {
console.error('Erreur lors du chargement du profil:', error);
toastr.error(
error.message || 'Erreur lors du chargement du profil',
'Erreur',
{ timeOut: 5000 }
);
} finally {
const profileContainer = document.getElementById('intervenant-profile-container') || document.getElementById('profile-form') || document.body;
if (window.CRVI_OVERLAY && profileContainer) { window.CRVI_OVERLAY.hide(profileContainer); }
}
}
/**
* Met à jour les checkboxes des jours de disponibilité (déjà générées en PHP)
*/
function updateJoursDisponibiliteCheckboxes(joursActuels) {
const joursValues = ['lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi', 'samedi', 'dimanche'];
joursValues.forEach(jour => {
const checkbox = document.getElementById(`jour-${jour}`);
if (checkbox) {
checkbox.checked = joursActuels.includes(jour);
}
});
}
/**
* Met à jour les checkboxes des heures de permanences
*/
function updateHeuresPermanencesCheckboxes(heuresActuelles) {
const heuresValues = ['09:00','10:00','11:00','12:00','13:00','14:00','15:00','16:00'];
heuresValues.forEach(h => {
const checkbox = document.getElementById(`heure-${h}`);
if (checkbox) {
checkbox.checked = Array.isArray(heuresActuelles) && heuresActuelles.includes(h);
}
});
}
/**
* Synchronise la liste JS des indisponibilités avec les données de l'API
* (les indisponibilités sont déjà affichées en PHP, on synchronise juste la liste interne)
*/
function syncIndisponibilitesList(indisponibilites) {
// La liste est déjà affichée en PHP, on synchronise juste la liste JS
// pour les opérations d'ajout/suppression
indisponibilitesList = indisponibilites || [];
// Mettre à jour l'affichage si des indisponibilités ont été ajoutées/supprimées
const container = document.getElementById('profile-indisponibilites-list');
const emptyContainer = document.getElementById('profile-indisponibilites-empty');
if (!container) return;
// Si la liste est vide, afficher le message
if (indisponibilitesList.length === 0) {
const existingItems = container.querySelectorAll('.indisponibilite-item');
existingItems.forEach(item => item.remove());
if (emptyContainer) {
emptyContainer.style.display = 'block';
}
} else {
if (emptyContainer) {
emptyContainer.style.display = 'none';
}
}
}
/**
* Affiche les indisponibilités ponctuelles (utilisée après ajout/suppression)
*/
function displayIndisponibilites(indisponibilites) {
const container = document.getElementById('profile-indisponibilites-list');
const emptyContainer = document.getElementById('profile-indisponibilites-empty');
if (!container) return;
if (!indisponibilites || indisponibilites.length === 0) {
container.innerHTML = '';
if (emptyContainer) {
emptyContainer.style.display = 'block';
}
return;
}
if (emptyContainer) {
emptyContainer.style.display = 'none';
}
// Types d'indisponibilité
const typesLabels = {
'conge': 'Congé',
'absence': 'Absence',
'maladie': 'Maladie'
};
container.innerHTML = indisponibilites.map((indisp, index) => {
const debut = indisp.debut || '';
const fin = indisp.fin || '';
const type = indisp.type || 'conge';
const typeLabel = typesLabels[type] || type;
const commentaire = indisp.commentaire || '';
// Badge classes selon type
let badgeClass = 'badge-indispo-unknown';
if (type === 'conge') badgeClass = 'badge-indispo-conge';
else if (type === 'absence') badgeClass = 'badge-indispo-absence';
else if (type === 'maladie') badgeClass = 'badge-indispo-maladie';
// Convertir les dates au format d/m/Y pour l'affichage
let debutFormatted = debut;
let finFormatted = fin;
// Si les dates sont au format YYYY-MM-DD, les convertir
if (debut && debut.match(/^\d{4}-\d{2}-\d{2}$/)) {
const dateObj = new Date(debut);
debutFormatted = dateObj.toLocaleDateString('fr-FR');
}
if (fin && fin.match(/^\d{4}-\d{2}-\d{2}$/)) {
const dateObj = new Date(fin);
finFormatted = dateObj.toLocaleDateString('fr-FR');
}
// Afficher la période
let periodeText = '';
if (debutFormatted && finFormatted) {
if (debutFormatted === finFormatted) {
periodeText = `Le ${debutFormatted}`;
} else {
periodeText = `Du ${debutFormatted} au ${finFormatted}`;
}
} else if (debutFormatted) {
periodeText = `Le ${debutFormatted}`;
}
return `
<div class="indisponibilite-item indispo-${type}" data-index="${index}">
<div>
<strong>${periodeText}</strong>
<span class="badge ${badgeClass} ms-2">${typeLabel}</span>
${commentaire ? `<p class="mb-0 text-muted mt-1">${commentaire}</p>` : ''}
</div>
<button type="button" class="btn-remove" onclick="removeIndisponibilite(${index})" title="Supprimer">
<i class="fas fa-times"></i>
</button>
</div>
`;
}).join('');
}
// Fonction globale pour supprimer une indisponibilité
window.removeIndisponibilite = function(index) {
if (confirm('Êtes-vous sûr de vouloir supprimer cette indisponibilité ?')) {
indisponibilitesList.splice(index, 1);
displayIndisponibilites(indisponibilitesList);
}
};
/**
* Affiche le formulaire d'ajout d'indisponibilité
*/
function showIndisponibiliteForm() {
const container = document.getElementById('indisponibilite-form-container');
if (container) {
container.style.display = 'block';
// Ajouter les attributs required quand le formulaire est visible
const debutInput = document.getElementById('indispo-debut');
const finInput = document.getElementById('indispo-fin');
const typeSelect = document.getElementById('indispo-type');
if (debutInput) debutInput.setAttribute('required', 'required');
if (finInput) finInput.setAttribute('required', 'required');
if (typeSelect) typeSelect.setAttribute('required', 'required');
// Réinitialiser les champs
if (debutInput) debutInput.value = '';
if (finInput) finInput.value = '';
if (typeSelect) typeSelect.value = '';
const commentaireTextarea = document.getElementById('indispo-commentaire');
if (commentaireTextarea) commentaireTextarea.value = '';
}
}
/**
* Cache le formulaire d'ajout d'indisponibilité
*/
function hideIndisponibiliteForm() {
const container = document.getElementById('indisponibilite-form-container');
if (container) {
container.style.display = 'none';
// Retirer les attributs required quand le formulaire est caché
const debutInput = document.getElementById('indispo-debut');
const finInput = document.getElementById('indispo-fin');
const typeSelect = document.getElementById('indispo-type');
if (debutInput) debutInput.removeAttribute('required');
if (finInput) finInput.removeAttribute('required');
if (typeSelect) typeSelect.removeAttribute('required');
}
}
/**
* Sauvegarde une nouvelle indisponibilité dans la liste
*/
function saveIndisponibilite() {
const debut = document.getElementById('indispo-debut').value;
const fin = document.getElementById('indispo-fin').value;
const type = document.getElementById('indispo-type').value;
const commentaire = document.getElementById('indispo-commentaire').value.trim();
// Validation
if (!debut || !fin || !type) {
toastr.warning('Veuillez remplir tous les champs obligatoires', 'Validation');
return;
}
// Vérifier que la date de fin est après la date de début
if (new Date(fin) < new Date(debut)) {
toastr.warning('La date de fin doit être supérieure ou égale à la date de début', 'Validation');
return;
}
// Convertir les dates au format d/m/Y pour ACF
const debutFormatted = formatDateToDMY(debut);
const finFormatted = formatDateToDMY(fin);
// Ajouter à la liste
indisponibilitesList.push({
debut: debutFormatted,
fin: finFormatted,
type: type,
commentaire: commentaire
});
// Rafraîchir l'affichage
displayIndisponibilites(indisponibilitesList);
// Cacher le formulaire
hideIndisponibiliteForm();
toastr.success('Indisponibilité ajoutée. N\'oubliez pas d\'enregistrer.', 'Succès', { timeOut: 3000 });
}
/**
* Convertit une date au format YYYY-MM-DD vers d/m/Y
*/
function formatDateToDMY(dateStr) {
if (!dateStr) return '';
const date = new Date(dateStr);
const day = String(date.getDate()).padStart(2, '0');
const month = String(date.getMonth() + 1).padStart(2, '0');
const year = date.getFullYear();
return `${day}/${month}/${year}`;
}
/**
* Convertit une date au format d/m/Y vers YYYY-MM-DD
*/
function formatDateToYMD(dateStr) {
if (!dateStr) return '';
// Format d/m/Y vers YYYY-MM-DD
const parts = dateStr.split('/');
if (parts.length === 3) {
return `${parts[2]}-${parts[1]}-${parts[0]}`;
}
return dateStr;
}
/**
* Affiche les départements
*/
function displayDepartements(departements) {
const container = document.getElementById('profile-departements');
if (!container) return;
if (!departements || departements.length === 0) {
container.innerHTML = '<span class="text-muted">Aucun département assigné</span>';
return;
}
// Les départements viennent comme objets {id, nom}
container.innerHTML = departements.map(dep => {
const nom = dep.nom || dep.post_title || `Département #${dep.id || dep.ID || dep}`;
return `<span class="badge bg-secondary me-2 mb-2">${nom}</span>`;
}).join('');
}
/**
* Affiche les spécialisations (types d'intervention)
*/
function displaySpecialisations(specialisations) {
const container = document.getElementById('profile-specialisations');
if (!container) return;
if (!specialisations || specialisations.length === 0) {
container.innerHTML = '<span class="text-muted">Aucune spécialisation assignée</span>';
return;
}
// Les spécialisations viennent comme objets {id, nom}
container.innerHTML = specialisations.map(spec => {
const nom = spec.nom || spec.post_title || `Spécialisation #${spec.id || spec.ID || spec}`;
return `<span class="badge bg-primary me-2 mb-2">${nom}</span>`;
}).join('');
}
/**
* Gère la soumission du formulaire de profil
*/
async function handleProfileSubmit(event) {
event.preventDefault();
const telephoneEl = document.getElementById('profile-telephone');
if (!telephoneEl) {
toastr.error('Champ téléphone non trouvé', 'Erreur');
return;
}
const telephone = telephoneEl.value.trim();
// Validation basique
if (!telephone) {
toastr.warning('Le numéro de téléphone est requis', 'Validation');
telephoneEl.focus();
return;
}
// Format de validation simple (à adapter selon les besoins)
const phoneRegex = /^[0-9+\s\-()]{8,}$/;
if (!phoneRegex.test(telephone)) {
toastr.warning('Format de numéro de téléphone invalide', 'Validation');
telephoneEl.focus();
return;
}
try {
// Désactiver le bouton de soumission
const submitBtn = event.target.querySelector('button[type="submit"]');
if (submitBtn) {
submitBtn.disabled = true;
submitBtn.innerHTML = '<span class="spinner-border spinner-border-sm me-2"></span>Enregistrement...';
}
const profileContainer = document.getElementById('intervenant-profile-container') || document.getElementById('profile-form') || document.body;
if (window.CRVI_OVERLAY && profileContainer) { window.CRVI_OVERLAY.show(profileContainer); }
await apiFetch('intervenant/profile', {
method: 'PUT',
body: JSON.stringify({
telephone: telephone
})
});
toastr.success(
'Profil mis à jour avec succès',
'Succès',
{ timeOut: 3000 }
);
// Recharger le profil pour avoir les données à jour
await loadProfile();
} catch (error) {
console.error('Erreur lors de la mise à jour du profil:', error);
toastr.error(
error.message || 'Erreur lors de la mise à jour du profil',
'Erreur',
{ timeOut: 5000 }
);
} finally {
// Réactiver le bouton
const submitBtn = event.target.querySelector('button[type="submit"]');
if (submitBtn) {
submitBtn.disabled = false;
submitBtn.innerHTML = '<i class="fas fa-save me-2"></i>Enregistrer les modifications';
}
const profileContainer = document.getElementById('intervenant-profile-container') || document.getElementById('profile-form') || document.body;
if (window.CRVI_OVERLAY && profileContainer) { window.CRVI_OVERLAY.hide(profileContainer); }
}
}
/**
* Gère la soumission du formulaire de disponibilités
*/
async function handleDisponibilitesSubmit(event) {
event.preventDefault();
try {
// Récupérer les jours de disponibilité cochés
const joursCheckboxes = document.querySelectorAll('input[name="jours_disponibilite[]"]:checked');
const joursDisponibilite = Array.from(joursCheckboxes).map(cb => cb.value);
// Récupérer les heures de permanences cochées
const heuresCheckboxes = document.querySelectorAll('input[name="heures_permanences[]"]:checked');
const heuresPermanences = Array.from(heuresCheckboxes).map(cb => cb.value);
// Désactiver le bouton de soumission
const submitBtn = event.target.querySelector('button[type="submit"]');
if (submitBtn) {
submitBtn.disabled = true;
submitBtn.innerHTML = '<span class="spinner-border spinner-border-sm me-2"></span>Enregistrement...';
}
const profileContainer = document.getElementById('intervenant-profile-container') || document.getElementById('disponibilites-form') || document.body;
if (window.CRVI_OVERLAY && profileContainer) { window.CRVI_OVERLAY.show(profileContainer); }
await apiFetch('intervenant/profile', {
method: 'PUT',
body: JSON.stringify({
jours_de_disponibilite: joursDisponibilite,
heures_de_permanences: heuresPermanences,
indisponibilites_ponctuelles: indisponibilitesList
})
});
toastr.success(
'Disponibilités mises à jour avec succès',
'Succès',
{ timeOut: 3000 }
);
// Vérifier s'il y a des conflits d'indisponibilités
await checkAndDisplayConflicts();
// Recharger le profil pour avoir les données à jour
await loadProfile();
} catch (error) {
console.error('Erreur lors de la mise à jour des disponibilités:', error);
toastr.error(
error.message || 'Erreur lors de la mise à jour des disponibilités',
'Erreur',
{ timeOut: 5000 }
);
} finally {
// Réactiver le bouton
const submitBtn = event.target.querySelector('button[type="submit"]');
if (submitBtn) {
submitBtn.disabled = false;
submitBtn.innerHTML = '<i class="fas fa-save me-2"></i>Enregistrer les disponibilités';
}
const profileContainer = document.getElementById('intervenant-profile-container') || document.getElementById('disponibilites-form') || document.body;
if (window.CRVI_OVERLAY && profileContainer) { window.CRVI_OVERLAY.hide(profileContainer); }
}
}
/**
* Vérifie et affiche les conflits d'indisponibilités après la sauvegarde
*/
async function checkAndDisplayConflicts() {
try {
// Faire une requête à l'API WordPress pour récupérer le message de conflit
const response = await apiFetch('intervenant/conflicts-check');
if (response && response.has_conflicts && response.message) {
// Afficher le message de conflit en haut de la page
displayConflictMessage(response.message);
}
} catch (error) {
console.error('Erreur lors de la vérification des conflits:', error);
// Ne pas afficher d'erreur à l'utilisateur pour ne pas perturber l'expérience
}
}
/**
* Affiche un message de conflit en haut de la page
* @param {string} message - Le message HTML à afficher
*/
function displayConflictMessage(message) {
// Trouver le conteneur où afficher le message (après le header, avant le contenu)
const container = document.querySelector('.crvi-intervenant-profile .container-fluid');
if (!container) return;
// Supprimer les anciens messages de conflit s'il y en a
const oldAlert = container.querySelector('.conflict-alert');
if (oldAlert) {
oldAlert.remove();
}
// Créer un nouvel élément d'alerte
const alertDiv = document.createElement('div');
alertDiv.className = 'conflict-alert alert alert-danger alert-dismissible fade show mt-3';
alertDiv.setAttribute('role', 'alert');
alertDiv.innerHTML = message;
// Ajouter un bouton de fermeture
const closeButton = document.createElement('button');
closeButton.type = 'button';
closeButton.className = 'btn-close';
closeButton.setAttribute('data-bs-dismiss', 'alert');
closeButton.setAttribute('aria-label', 'Close');
alertDiv.appendChild(closeButton);
// Insérer le message après la navigation (row mb-4)
const navRow = container.querySelector('.row.mb-4');
if (navRow && navRow.nextSibling) {
container.insertBefore(alertDiv, navRow.nextSibling);
} else {
container.insertBefore(alertDiv, container.firstChild);
}
// Scroller vers le haut pour voir le message
window.scrollTo({
top: 0,
behavior: 'smooth'
});
}