From 80618e0b9648d31241830514935e036f5f092621 Mon Sep 17 00:00:00 2001 From: theShlavuk Date: Wed, 21 Jan 2026 21:11:35 +0100 Subject: [PATCH] corections --- app/models/Event_Model.php | 35 +++- assets/js/modules/agenda-admin-permanences.js | 157 +++++++++++++++++- assets/js/modules/agenda-modal-forms.js | 10 ++ 3 files changed, 198 insertions(+), 4 deletions(-) diff --git a/app/models/Event_Model.php b/app/models/Event_Model.php index 2713116..135d6f7 100644 --- a/app/models/Event_Model.php +++ b/app/models/Event_Model.php @@ -845,6 +845,7 @@ class CRVI_Event_Model extends Main_Model { 'id_intervenant' => $existing_event->id_intervenant ?? null, 'id_traducteur' => $existing_event->id_traducteur ?? null, 'id_local' => $existing_event->id_local ?? null, + 'langues_disponibles' => $existing_event->langues_disponibles ?? null, ], $data); // 5. Valider les données fusionnées (aligné avec create_event) @@ -949,6 +950,38 @@ class CRVI_Event_Model extends Main_Model { // 7. Calculer automatiquement le flag assign $update_data['assign'] = self::calculate_assign_flag($update_data); + // 7.5. Traiter langues_disponibles UNIQUEMENT pour les permanences + // Ne pas traiter pour les autres types d'événements pour ne pas casser la mise à jour classique + if (($update_data['type'] ?? '') === 'permanence') { + $langues_disponibles_value = ''; + + // Si c'est un tableau (langues), le convertir en chaîne séparée par virgules + if (isset($data['langues']) && is_array($data['langues']) && !empty($data['langues'])) { + // Nettoyer et valider les slugs de langues + $langues_valides = []; + foreach ($data['langues'] as $langue_slug) { + $langue_slug = sanitize_text_field($langue_slug); + if (!empty($langue_slug)) { + $langues_valides[] = $langue_slug; + } + } + // Joindre les langues avec le séparateur virgule (,) + if (!empty($langues_valides)) { + $langues_disponibles_value = implode(',', $langues_valides); + } + } + // Si c'est déjà une chaîne (langues_disponibles) - pour mise à jour directe + elseif (isset($data['langues_disponibles'])) { + $langues_disponibles_value = sanitize_text_field($data['langues_disponibles']); + } + // Si aucune langue n'est envoyée, conserver la valeur existante (déjà dans $update_data via array_merge) + + // Mettre à jour seulement si une valeur a été fournie + if (isset($data['langues']) || isset($data['langues_disponibles'])) { + $update_data['langues_disponibles'] = $langues_disponibles_value; + } + } + // 8. Ajouter les métadonnées de modification $update_data['modifie_par'] = get_current_user_id(); $update_data['date_modification'] = current_time('mysql'); @@ -959,7 +992,7 @@ class CRVI_Event_Model extends Main_Model { 'date_rdv', 'heure_rdv', 'date_fin', 'heure_fin', 'type', 'statut', 'motif_annulation', 'commentaire', 'id_departement', 'id_type_intervention', 'langue', 'id_beneficiaire', 'id_intervenant', 'id_traducteur', 'id_local', - 'assign', 'modifie_par', 'date_modification' + 'langues_disponibles', 'assign', 'modifie_par', 'date_modification' ]; $filtered_data = []; diff --git a/assets/js/modules/agenda-admin-permanences.js b/assets/js/modules/agenda-admin-permanences.js index 70559d4..b72f51a 100644 --- a/assets/js/modules/agenda-admin-permanences.js +++ b/assets/js/modules/agenda-admin-permanences.js @@ -31,7 +31,13 @@ export function initializeAdminPermanences() { } // Initialiser Select2 pour le champ langues - initializeSelect2(); + // Utiliser un petit délai pour s'assurer que le DOM est complètement prêt + setTimeout(() => { + initializeSelect2(); + }, 100); + + // Réinitialiser Select2 quand l'onglet devient visible (problème avec éléments cachés) + setupSelect2TabListener(); // Écouter les changements pour mettre à jour l'aperçu setupPreviewListeners(); @@ -46,13 +52,109 @@ export function initializeAdminPermanences() { initializeCsvImport(); } +// Variable de garde pour éviter les initialisations multiples simultanées +let isInitializingSelect2 = false; +let select2Initialized = false; // Garde globale pour éviter toute réinitialisation + /** * Initialise Select2 pour le champ de sélection des langues */ function initializeSelect2() { + console.log('🔍 initializeSelect2() appelée - Stack:', new Error().stack); + + // Éviter les appels multiples simultanés + if (isInitializingSelect2) { + console.log('⏳ Initialisation Select2 déjà en cours, attente...'); + return; + } + + // Si Select2 est déjà initialisé et fonctionne, ne pas réinitialiser const languesSelect = document.getElementById('langues-permanences'); - if (languesSelect && typeof jQuery !== 'undefined' && jQuery.fn.select2) { - jQuery(languesSelect).select2({ + if (languesSelect && select2Initialized) { + const $select = jQuery(languesSelect); + if ($select.data('select2') && $select.hasClass('select2-hidden-accessible')) { + console.log('✅ Select2 déjà initialisé (flag=true), pas de réinitialisation'); + return; + } + } + + if (!languesSelect) { + console.warn('Select langues-permanences non trouvé'); + return; + } + + // Vérifier que jQuery et Select2 sont disponibles + if (typeof jQuery === 'undefined' || !jQuery.fn.select2) { + console.warn('jQuery ou Select2 non disponible, réessai dans 100ms...'); + setTimeout(initializeSelect2, 100); + return; + } + + const $select = jQuery(languesSelect); + + // DESTRUCTION AGRESSIVE : Détruire tout Select2 au début, sans pitié ! + try { + // Détruire l'instance Select2 si elle existe + if ($select.data('select2')) { + $select.select2('destroy'); + } + + // Supprimer TOUS les conteneurs Select2 dans le parent + const parentContainer = languesSelect.closest('.card-body') || languesSelect.parentElement; + if (parentContainer) { + // Supprimer tous les conteneurs Select2 + const select2Containers = parentContainer.querySelectorAll('span.select2-container'); + select2Containers.forEach(container => container.remove()); + } + + // Nettoyer tous les attributs Select2 du select + $select.removeClass('select2-hidden-accessible'); + $select.removeAttr('data-select2-id'); + $select.removeAttr('aria-hidden'); + $select.removeAttr('tabindex'); + + console.log('💥 Select2 complètement détruit (méchamment)'); + } catch (e) { + console.warn('Erreur lors de la destruction agressive:', e); + } + + // Marquer qu'on est en train d'initialiser + isInitializingSelect2 = true; + select2Initialized = false; // Réinitialiser le flag + + // Vérifier que le select a des options + const options = languesSelect.querySelectorAll('option'); + if (options.length === 0) { + console.warn('Le select langues-permanences n\'a pas d\'options'); + isInitializingSelect2 = false; + return; + } + + // Initialiser Select2 + try { + // Vérifier que les options sont valides + const validOptions = Array.from(options).filter(opt => { + return opt.value && opt.value.trim() !== '' && opt.textContent && opt.textContent.trim() !== ''; + }); + + console.log('📋 Options valides trouvées:', validOptions.length, 'sur', options.length); + + if (validOptions.length === 0) { + console.error('Aucune option valide trouvée dans le select'); + return; + } + + // S'assurer que le select est visible avant l'initialisation + const isVisible = languesSelect.offsetParent !== null; + if (!isVisible) { + console.warn('Le select langues-permanences n\'est pas visible, réessai dans 200ms...'); + setTimeout(initializeSelect2, 200); + return; + } + + // Initialiser Select2 - laisser Select2 lire automatiquement depuis le select natif + // Ne pas passer data pour éviter les conflits avec le select natif + $select.select2({ placeholder: 'Sélectionnez une ou plusieurs langues', allowClear: true, width: '100%', @@ -62,9 +164,58 @@ function initializeSelect2() { } } }); + + // Vérifier que Select2 a bien chargé les options + setTimeout(() => { + const select2Instance = $select.data('select2'); + if (select2Instance) { + // Forcer Select2 à recharger les options depuis le select natif + $select.trigger('change.select2'); + console.log('✅ Select2 initialisé pour langues-permanences avec', validOptions.length, 'options'); + // Marquer comme initialisé + select2Initialized = true; + } else { + console.warn('⚠️ Select2 n\'a pas été correctement initialisé'); + select2Initialized = false; + } + // Libérer la garde + isInitializingSelect2 = false; + }, 50); + } catch (e) { + console.error('Erreur lors de l\'initialisation de Select2:', e); + isInitializingSelect2 = false; + select2Initialized = false; } } +/** + * Configure un listener pour réinitialiser Select2 quand l'onglet devient visible + * (uniquement si Select2 n'est pas déjà correctement initialisé) + */ +function setupSelect2TabListener() { + const tabButtons = document.querySelectorAll('#permanences-tabs button[data-bs-toggle="tab"]'); + tabButtons.forEach(button => { + button.addEventListener('shown.bs.tab', function(e) { + // Vérifier si l'onglet qui devient visible contient le select langues-permanences + const targetTabId = e.target.getAttribute('data-bs-target'); + const targetTab = document.querySelector(targetTabId); + + if (targetTab && targetTab.querySelector('#langues-permanences')) { + // Ne PAS réinitialiser si Select2 est déjà initialisé + // La garde globale select2Initialized empêche toute réinitialisation + if (!select2Initialized) { + console.log('🔄 Onglet changé, vérification Select2...'); + setTimeout(() => { + initializeSelect2(); + }, 100); + } else { + console.log('✅ Select2 déjà initialisé, pas de réinitialisation'); + } + } + }); + }); +} + /** * Configure la préselection des inputs jours[] et heures[] * à partir des attributs data-days et data-time-slots de l'option sélectionnée diff --git a/assets/js/modules/agenda-modal-forms.js b/assets/js/modules/agenda-modal-forms.js index a1d9fd5..57a1c24 100644 --- a/assets/js/modules/agenda-modal-forms.js +++ b/assets/js/modules/agenda-modal-forms.js @@ -438,6 +438,16 @@ export async function handleEventFormSubmit(mode, eventId = null, onSuccess = nu const formData = new FormData(form); let data = Object.fromEntries(formData.entries()); + // Gérer les champs multiples (comme langues[]) UNIQUEMENT pour les permanences + // Ne pas traiter pour les autres types d'événements pour ne pas casser la mise à jour classique + if (data.type === 'permanence') { + const langues = formData.getAll('langues[]'); + if (langues && langues.length > 0) { + // Filtrer les valeurs vides et convertir en tableau de slugs + data.langues = langues.filter(value => value !== '' && value !== null); + } + } + // Validation côté client const errors = validateForm(data, mode); if (Object.keys(errors).length > 0) {