diff --git a/assets/css/crvi-agenda.css b/assets/css/crvi-agenda.css index ee7579b..dc2536d 100644 --- a/assets/css/crvi-agenda.css +++ b/assets/css/crvi-agenda.css @@ -142,4 +142,268 @@ .fc-more-popover { max-width: 300px !important; } +} + +/* ======================================== + Timeline pour historique bénéficiaire + ======================================== */ + +.crvi-timeline { + display: flex; + flex-direction: column; + margin: 20px auto; + padding-left: 80px; + position: relative; +} + +.crvi-timeline__event { + background: #f8f9fa; + margin-bottom: 30px; + position: relative; + display: flex; + border-radius: 8px; + box-shadow: 0 15px 30px -6px rgba(50, 50, 93, 0.15), + 0 9px 18px -9px rgba(0, 0, 0, 0.2); + transition: transform 0.2s ease, box-shadow 0.2s ease; +} + +.crvi-timeline__event:hover { + transform: translateY(-2px); + box-shadow: 0 20px 40px -8px rgba(50, 50, 93, 0.2), + 0 12px 24px -12px rgba(0, 0, 0, 0.25); +} + +/* Ligne verticale qui connecte les événements */ +.crvi-timeline__event:after { + content: ""; + width: 3px; + height: 100%; + background: #dee2e6; + position: absolute; + top: 50%; + left: -57px; + z-index: 0; +} + +/* Cercle sur la ligne pour chaque événement */ +.crvi-timeline__event:before { + content: ""; + width: 50px; + height: 50px; + position: absolute; + background: #fff; + border-radius: 100%; + left: -82px; + top: 50%; + transform: translateY(-50%); + border: 3px solid #dee2e6; + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); + z-index: 1; +} + +/* Masquer la ligne après le dernier élément */ +.crvi-timeline__event--last:after { + content: none; +} + +/* Section icône/date à gauche */ +.crvi-timeline__event__icon { + border-radius: 8px 0 0 8px; + background: #e9ecef; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + min-width: 120px; + padding: 20px; + position: relative; +} + +.crvi-timeline__event__icon i { + font-size: 2rem; + margin-bottom: 10px; + color: #6c757d; +} + +.crvi-timeline__event__date { + color: #495057; + font-size: 0.85rem; + font-weight: 600; + text-align: center; + line-height: 1.4; +} + +/* Contenu de l'événement */ +.crvi-timeline__event__content { + padding: 20px; + flex-grow: 1; + background: #fff; + border-radius: 0 8px 8px 0; +} + +.crvi-timeline__event__title { + font-size: 1.1rem; + line-height: 1.4; + text-transform: uppercase; + font-weight: 600; + color: #495057; + letter-spacing: 0.5px; + margin-bottom: 15px; +} + +.crvi-timeline__event__description { + color: #525f7f; + font-size: 0.9rem; + line-height: 1.6; +} + +/* Type d'événement : Par défaut (gris clair) */ +.crvi-timeline__event--default { + background: #f8f9fa; +} + +.crvi-timeline__event--default:before { + background: #e9ecef; + border-color: #adb5bd; +} + +.crvi-timeline__event--default:after { + background: #adb5bd; +} + +.crvi-timeline__event--default .crvi-timeline__event__icon { + background: #e9ecef; +} + +.crvi-timeline__event--default .crvi-timeline__event__icon i { + color: #6c757d; +} + +.crvi-timeline__event--default .crvi-timeline__event__date { + color: #495057; +} + +.crvi-timeline__event--default .crvi-timeline__event__title { + color: #495057; +} + +/* Type d'événement : Prévu (bleu) */ +.crvi-timeline__event--prevu { + background: #e7f1ff; +} + +.crvi-timeline__event--prevu:before { + background: #cfe2ff; + border-color: #0d6efd; +} + +.crvi-timeline__event--prevu:after { + background: #0d6efd; +} + +.crvi-timeline__event--prevu .crvi-timeline__event__icon { + background: #0d6efd; +} + +.crvi-timeline__event--prevu .crvi-timeline__event__icon i { + color: #fff; +} + +.crvi-timeline__event--prevu .crvi-timeline__event__date { + color: #fff; +} + +.crvi-timeline__event--prevu .crvi-timeline__event__title { + color: #0d6efd; +} + +/* Type d'événement : Présent (vert) */ +.crvi-timeline__event--present { + background: #d1f4e0; +} + +.crvi-timeline__event--present:before { + background: #a3e7c2; + border-color: #198754; +} + +.crvi-timeline__event--present:after { + background: #198754; +} + +.crvi-timeline__event--present .crvi-timeline__event__icon { + background: #198754; +} + +.crvi-timeline__event--present .crvi-timeline__event__icon i { + color: #fff; +} + +.crvi-timeline__event--present .crvi-timeline__event__date { + color: #fff; +} + +.crvi-timeline__event--present .crvi-timeline__event__title { + color: #198754; +} + +/* Type d'événement : Incident/Absence (rouge) */ +.crvi-timeline__event--incident { + background: #ffe5e5; +} + +.crvi-timeline__event--incident:before { + background: #ffccd5; + border-color: #dc3545; +} + +.crvi-timeline__event--incident:after { + background: #dc3545; +} + +.crvi-timeline__event--incident .crvi-timeline__event__icon { + background: #dc3545; +} + +.crvi-timeline__event--incident .crvi-timeline__event__icon i { + color: #fff; +} + +.crvi-timeline__event--incident .crvi-timeline__event__date { + color: #fff; +} + +.crvi-timeline__event--incident .crvi-timeline__event__title { + color: #dc3545; +} + +/* Responsive */ +@media (max-width: 768px) { + .crvi-timeline { + padding-left: 40px; + } + + .crvi-timeline__event { + flex-direction: column; + } + + .crvi-timeline__event__icon { + border-radius: 8px 8px 0 0; + min-width: auto; + } + + .crvi-timeline__event__content { + border-radius: 0 0 8px 8px; + } + + .crvi-timeline__event:before { + width: 30px; + height: 30px; + left: -42px; + } + + .crvi-timeline__event:after { + left: -27px; + width: 2px; + } } \ No newline at end of file diff --git a/assets/js/modules/agenda-modal-buttons.js b/assets/js/modules/agenda-modal-buttons.js index f52fba8..3625031 100644 --- a/assets/js/modules/agenda-modal-buttons.js +++ b/assets/js/modules/agenda-modal-buttons.js @@ -1,7 +1,7 @@ // Module de gestion des boutons de la modale // Contient les gestionnaires d'événements pour tous les boutons de la modale -import { deleteEvent, changeEventStatus } from './agenda-api.js'; +import { deleteEvent, changeEventStatus, apiFetch } from './agenda-api.js'; import { notifyError, notifySuccess } from './agenda-notifications.js'; import { populateSelects } from './agenda-modal-select.js'; import { fillFormWithEvent, clearFormErrors, handleEventFormSubmit } from './agenda-modal-forms.js'; @@ -414,6 +414,140 @@ export function initializeReportIncidentButton(getCurrentEventData, openSubModal }; } +/** + * Charge l'historique des 3 derniers rendez-vous d'un bénéficiaire + * @param {number} beneficiaireId - ID du bénéficiaire + * @returns {Promise} Tableau des rendez-vous avec leurs incidents + */ +async function loadBeneficiaireHistorique(beneficiaireId) { + try { + const historique = await apiFetch(`beneficiaires/${beneficiaireId}/historique`); + return historique || []; + } catch (error) { + console.error('Erreur lors du chargement de l\'historique:', error); + notifyError('Erreur lors du chargement de l\'historique du bénéficiaire'); + return []; + } +} + +/** + * Affiche l'historique sous forme de ligne du temps verticale + * @param {Array} historiqueData - Tableau des rendez-vous avec leurs incidents + */ +function displayHistoriqueTimeline(historiqueData) { + const timelineContainer = document.getElementById('historiqueTimeline'); + if (!timelineContainer) { + console.error('Conteneur de timeline non trouvé'); + return; + } + + if (!historiqueData || historiqueData.length === 0) { + timelineContainer.innerHTML = ` +
+ + Aucun rendez-vous trouvé pour ce bénéficiaire. +
+ `; + return; + } + + let html = '
'; + + historiqueData.forEach((rdv, index) => { + const date = new Date(rdv.date_rdv + 'T' + rdv.heure_rdv); + const dateFormatted = date.toLocaleDateString('fr-FR', { + day: '2-digit', + month: '2-digit', + year: 'numeric' + }); + const heureFormatted = date.toLocaleTimeString('fr-FR', { + hour: '2-digit', + minute: '2-digit' + }); + + const intervenantNom = rdv.intervenant + ? `${rdv.intervenant.nom || ''} ${rdv.intervenant.prenom || ''}`.trim() + : 'Non renseigné'; + + const typeInterventionNom = rdv.type_intervention?.nom || 'Non renseigné'; + + // Déterminer le type d'événement et la couleur + const hasIncident = rdv.incident && rdv.incident.id; + const statutLower = rdv.statut ? rdv.statut.toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, "") : ''; + + let eventType = 'default'; + let icon = 'fa-calendar-alt'; + + if (hasIncident || statutLower.includes('absence') || statutLower.includes('absent')) { + eventType = 'incident'; // Rouge + icon = 'fa-exclamation-triangle'; + } else if (statutLower.includes('present')) { + eventType = 'present'; // Vert + icon = 'fa-check-circle'; + } else if (statutLower.includes('prevu')) { + eventType = 'prevu'; // Bleu + icon = 'fa-clock'; + } else { + eventType = 'default'; // Gris clair + icon = 'fa-calendar-alt'; + } + + const isLastItem = index === historiqueData.length - 1; + + html += ` +
+
+ +
+ ${dateFormatted}
${heureFormatted} +
+
+
+
+ ${hasIncident ? '' : ''} + ${rdv.type || 'Rendez-vous'} +
+
+
+ Intervenant: ${intervenantNom} +
+
+ Type d'intervention: ${typeInterventionNom} +
+ ${rdv.statut ? ` +
+ Statut: + ${rdv.statut} +
+ ` : ''} + ${rdv.commentaire ? ` +
+ Commentaire: ${rdv.commentaire} +
+ ` : ''} + ${hasIncident ? ` +
+ Incident signalé +
+ Résumé: ${rdv.incident.resume_incident || 'Non renseigné'} +
+ ${rdv.incident.commentaire_incident ? ` +
+ Commentaire: ${rdv.incident.commentaire_incident} +
+ ` : ''} +
+ ` : ''} +
+
+
+ `; + }); + + html += '
'; + timelineContainer.innerHTML = html; +} + /** * Initialise le bouton "Historique bénéficiaire" * @param {Function} getCurrentEventData - Fonction pour obtenir les données de l'événement @@ -450,8 +584,21 @@ export function initializeHistoriqueButton(getCurrentEventData, openSubModal) { `; } - // Charger l'historique (la fonction doit être importée depuis agenda-modal.js) - // Pour l'instant on laisse vide, le chargement sera fait dans le modal principal + // Charger l'historique + try { + const historiqueData = await loadBeneficiaireHistorique(parseInt(beneficiaireId)); + displayHistoriqueTimeline(historiqueData); + } catch (error) { + console.error('Erreur lors du chargement de l\'historique:', error); + if (timelineContainer) { + timelineContainer.innerHTML = ` +
+ + Erreur lors du chargement de l'historique. +
+ `; + } + } }, (subModal) => { // Nettoyer après fermeture