Spaces:
Running
Running
| // js/proa.js - Lógica para el visor de Guías PROA (v12 - Con Diagnósticos) | |
| // ====================================================================== | |
| // BLOCK START: DOM Elements Selection & Initial Setup | |
| // ====================================================================== | |
| console.log("[proa.js v12] Script cargado. Esperando DOMContentLoaded..."); | |
| window.addEventListener('DOMContentLoaded', () => { | |
| console.log("[proa.js v12] DOMContentLoaded detectado. Iniciando setup..."); | |
| // Selectores y Contenedores | |
| const guideSelector = document.getElementById('guideSelector'); | |
| const diagnosisSelectorContainer = document.getElementById('diagnosis-selector-container'); | |
| const diagnosisSelector = document.getElementById('diagnosisSelector'); | |
| const guideContentDisplay = document.getElementById('guide-content-display'); | |
| const togglePediatricCheckbox = document.getElementById('togglePediatric'); | |
| const guidesDataUrl = 'guides_data.json'; // Asume que está en proa/ | |
| // Verificar elementos críticos | |
| if (!guideSelector || !diagnosisSelectorContainer || !diagnosisSelector || !guideContentDisplay || !togglePediatricCheckbox) { | |
| console.error("[proa.js v12] Error Crítico: No se encontraron elementos DOM esenciales."); | |
| guideContentDisplay.innerHTML = '<p class="text-red-500 font-semibold text-center py-10">Error: Interfaz no inicializada correctamente.</p>'; | |
| return; | |
| } | |
| console.log("[proa.js v12] Elementos DOM encontrados."); | |
| // ====================================================================== | |
| // BLOCK END: DOM Elements Selection & Initial Setup | |
| // ====================================================================== | |
| // ====================================================================== | |
| // BLOCK START: State Variables | |
| // ====================================================================== | |
| let allGuides = []; // Almacenará todas las guías del JSON | |
| let currentSelectedGuideData = null; // Guarda los datos completos de la guía seleccionada | |
| let currentGuideHTMLContent = null; // Cache para el HTML de la guía actual con diagnósticos | |
| // ====================================================================== | |
| // BLOCK END: State Variables | |
| // ====================================================================== | |
| // ====================================================================== | |
| // BLOCK START: Function to Fetch Guides Data (from JSON) | |
| // ====================================================================== | |
| async function loadGuidesData() { | |
| console.log(`[proa.js v12] Iniciando loadGuidesData desde: ${guidesDataUrl}`); | |
| guideSelector.innerHTML = '<option value="">Cargando...</option>'; | |
| guideSelector.disabled = true; | |
| resetUI(); // Limpia todo | |
| try { | |
| console.log("[proa.js v12] Realizando fetch..."); | |
| const response = await fetch(guidesDataUrl); | |
| console.log(`[proa.js v12] Fetch completado. Status: ${response.status}`); | |
| if (!response.ok) throw new Error(`Error HTTP ${response.status} al cargar ${guidesDataUrl}. Verifica que el archivo exista y sea accesible.`); | |
| console.log("[proa.js v12] Parseando JSON..."); | |
| const rawData = await response.json(); | |
| console.log("[proa.js v12] JSON parseado."); | |
| if (!Array.isArray(rawData)) throw new Error("El formato del JSON no es un array válido."); | |
| // Filtrar y validar guías | |
| allGuides = rawData.filter(g => g.id && g.title && g.file && typeof g.isPediatric === 'boolean'); | |
| console.log(`[proa.js v12] Guías válidas iniciales: ${allGuides.length}`); | |
| // Validar estructura de diagnóstico si existe | |
| allGuides = allGuides.map(g => { | |
| if (g.hasDiagnoses === true) { | |
| if (!Array.isArray(g.diagnoses) || g.diagnoses.some(d => !d.id || !d.title)) { | |
| console.warn(`[proa.js v12] Guía '${g.title}' marcada con 'hasDiagnoses' pero la estructura 'diagnoses' es inválida. Se tratará como guía normal.`); | |
| return { ...g, hasDiagnoses: false, diagnoses: undefined }; // Corregir datos inválidos | |
| } | |
| } | |
| return g; | |
| }); | |
| if (allGuides.length === 0) throw new Error("No se encontraron guías válidas en los datos después de la validación."); | |
| // Ordenar alfabéticamente para el desplegable principal | |
| allGuides.sort((a, b) => a.title.localeCompare(b.title)); | |
| console.log("[proa.js v12] Llamando a populateGuideSelector (mostrará Adultos por defecto)..."); | |
| populateGuideSelector(); // Poblar el desplegable principal | |
| guideSelector.disabled = false; // Habilitar selector principal | |
| console.log("[proa.js v12] Carga inicial de datos completada."); | |
| } catch (error) { | |
| console.error("[proa.js v12] Error durante loadGuidesData:", error); | |
| guideSelector.innerHTML = `<option value="">Error al cargar</option>`; | |
| guideSelector.disabled = true; | |
| guideContentDisplay.innerHTML = `<p class="text-red-600 font-semibold text-center py-10">Error crítico al cargar datos: ${error.message}. Revisa la consola y el archivo '${guidesDataUrl}'.</p>`; | |
| } | |
| } | |
| // ====================================================================== | |
| // BLOCK END: Function to Fetch Guides Data (from JSON) | |
| // ====================================================================== | |
| // ====================================================================== | |
| // BLOCK START: UI Update Functions | |
| // ====================================================================== | |
| // Resetea el área de contenido y el selector de diagnóstico | |
| function resetUI(fullReset = true) { | |
| guideContentDisplay.innerHTML = '<div class="text-center py-16 text-gray-400"><i class="fas fa-file-alt text-5xl mb-4"></i><p>Selecciona una guía del desplegable.</p></div>'; | |
| diagnosisSelector.innerHTML = '<option value="">-- Seleccione Diagnóstico --</option>'; // Limpiar opciones | |
| diagnosisSelectorContainer.classList.add('hidden'); // Ocultar contenedor | |
| currentSelectedGuideData = null; | |
| currentGuideHTMLContent = null; // Limpiar cache de HTML | |
| if(fullReset && guideSelector.options.length > 1) { | |
| guideSelector.value = ""; // Resetear también el selector principal si no es el reset inicial | |
| } | |
| } | |
| // Llena el selector PRINCIPAL de guías aplicando el filtro Pediátrico/Adulto | |
| function populateGuideSelector() { | |
| const showOnlyPediatric = togglePediatricCheckbox.checked; | |
| console.log(`[proa.js v12] Poblando selector principal. Mostrar Pediátricas: ${showOnlyPediatric}`); | |
| // Filtrar por Adulto o Pediátrico | |
| const filteredGuides = allGuides.filter(guide => guide.isPediatric === showOnlyPediatric); | |
| // Ordenar alfabéticamente (ya deberían estar ordenadas, pero por si acaso) | |
| filteredGuides.sort((a, b) => a.title.localeCompare(b.title)); | |
| // Limpiar selector principal | |
| guideSelector.innerHTML = `<option value="">-- Seleccione Guía (${showOnlyPediatric ? 'Pediátricas' : 'Adultos'}) --</option>`; | |
| if (filteredGuides.length > 0) { | |
| const fragment = document.createDocumentFragment(); | |
| filteredGuides.forEach(guide => { | |
| const option = document.createElement('option'); | |
| option.value = guide.id; // Usar el ID único de la guía como valor | |
| option.textContent = guide.isPediatric ? `${guide.title} (PED)` : guide.title; | |
| // Guardar datos importantes en el dataset para fácil acceso | |
| option.dataset.hasDiagnoses = guide.hasDiagnoses || false; | |
| option.dataset.file = guide.file; | |
| fragment.appendChild(option); | |
| }); | |
| guideSelector.appendChild(fragment); | |
| console.log(`[proa.js v12] Selector principal poblado con ${filteredGuides.length} guías.`); | |
| } else { | |
| guideSelector.innerHTML = `<option value="">-- No hay guías ${showOnlyPediatric ? 'Pediátricas' : 'Adultos'} --</option>`; | |
| console.log(`[proa.js v12] No se encontraron guías para el filtro actual.`); | |
| } | |
| resetUI(false); // Resetear UI secundaria sin deseleccionar la guía principal | |
| } | |
| // Puebla el selector de DIAGNÓSTICOS | |
| function populateDiagnosisSelector(guideData) { | |
| console.log(`[proa.js v12] Poblando selector de diagnósticos para: ${guideData.title}`); | |
| diagnosisSelector.innerHTML = '<option value="">-- Seleccione Diagnóstico --</option>'; // Limpiar | |
| if (guideData.hasDiagnoses && Array.isArray(guideData.diagnoses)) { | |
| const fragment = document.createDocumentFragment(); | |
| guideData.diagnoses.forEach(diagnosis => { | |
| const option = document.createElement('option'); | |
| option.value = diagnosis.id; // Usar el ID del diagnóstico como valor | |
| option.textContent = diagnosis.title; | |
| fragment.appendChild(option); | |
| }); | |
| diagnosisSelector.appendChild(fragment); | |
| diagnosisSelectorContainer.classList.remove('hidden'); // Mostrar contenedor | |
| console.log(`[proa.js v12] Selector de diagnósticos poblado con ${guideData.diagnoses.length} opciones.`); | |
| } else { | |
| console.warn("[proa.js v12] Se intentó poblar diagnósticos para una guía sin ellos o con datos inválidos."); | |
| diagnosisSelectorContainer.classList.add('hidden'); // Asegurarse que esté oculto | |
| } | |
| } | |
| // ====================================================================== | |
| // BLOCK END: UI Update Functions | |
| // ====================================================================== | |
| // ====================================================================== | |
| // BLOCK START: Content Loading Functions | |
| // ====================================================================== | |
| // Carga el CONTENIDO COMPLETO de una guía (para guías SIN diagnósticos) | |
| async function loadFullGuideContent(guideFile) { | |
| if (!guideFile) { | |
| resetUI(); | |
| return; | |
| } | |
| console.log(`[proa.js v12] Solicitando contenido COMPLETO de: ${guideFile}`); | |
| guideContentDisplay.innerHTML = '<div class="text-center py-20"><i class="fas fa-spinner fa-spin text-3xl text-gray-400"></i><p class="mt-2 text-gray-500">Cargando guía completa...</p></div>'; | |
| currentGuideHTMLContent = null; // Limpiar cache | |
| try { | |
| const response = await fetch(guideFile); | |
| if (!response.ok) throw new Error(`Error HTTP ${response.status} al cargar ${guideFile}`); | |
| const htmlText = await response.text(); | |
| // Parsear y extraer contenido principal (mismo método que antes) | |
| const parser = new DOMParser(); | |
| const doc = parser.parseFromString(htmlText, 'text/html'); | |
| // Intentar selector específico, si no, usar body | |
| const contentNode = doc.querySelector('.max-w-4xl.mx-auto.px-4.py-8') || doc.body; | |
| if (contentNode) { | |
| const clonedContent = contentNode.cloneNode(true); | |
| // Eliminar scripts del contenido clonado | |
| const scripts = clonedContent.querySelectorAll('script'); | |
| scripts.forEach(script => script.remove()); | |
| if (scripts.length > 0) console.log(`[proa.js v12] Eliminados ${scripts.length} script(s) de ${guideFile}.`); | |
| guideContentDisplay.innerHTML = ''; // Limpiar área | |
| guideContentDisplay.appendChild(clonedContent); // Añadir contenido limpio | |
| console.log(`[proa.js v12] Contenido COMPLETO de ${guideFile} mostrado.`); | |
| guideContentDisplay.scrollTop = 0; // Ir al inicio del contenido | |
| } else { | |
| throw new Error(`No se encontró nodo de contenido principal en '${guideFile}'.`); | |
| } | |
| } catch (error) { | |
| console.error(`[proa.js v12] Error al cargar/mostrar contenido COMPLETO de ${guideFile}:`, error); | |
| guideContentDisplay.innerHTML = `<div class="text-center py-20 text-red-600">Error al cargar contenido: ${error.message}</div>`; | |
| } | |
| } | |
| // Carga y cachea el HTML de una guía CON diagnósticos, luego muestra el diagnóstico especificado | |
| async function loadAndDisplayDiagnosisContent(guideData, diagnosisId) { | |
| console.log(`[proa.js v12] Solicitando diagnóstico '${diagnosisId}' de la guía '${guideData.title}' (${guideData.file})`); | |
| guideContentDisplay.innerHTML = '<div class="text-center py-20"><i class="fas fa-spinner fa-spin text-3xl text-gray-400"></i><p class="mt-2 text-gray-500">Cargando diagnóstico...</p></div>'; | |
| try { | |
| // Si el HTML de la guía no está cacheado, cargarlo | |
| if (!currentGuideHTMLContent) { | |
| console.log(`[proa.js v12] HTML de ${guideData.file} no cacheado. Realizando fetch...`); | |
| const response = await fetch(guideData.file); | |
| if (!response.ok) throw new Error(`Error HTTP ${response.status} al cargar ${guideData.file}`); | |
| currentGuideHTMLContent = await response.text(); | |
| console.log(`[proa.js v12] HTML de ${guideData.file} cargado y cacheado.`); | |
| } else { | |
| console.log(`[proa.js v12] Usando HTML cacheado de ${guideData.file}.`); | |
| } | |
| // Parsear el HTML (cacheado o recién cargado) | |
| const parser = new DOMParser(); | |
| const doc = parser.parseFromString(currentGuideHTMLContent, 'text/html'); | |
| // Buscar el div específico del diagnóstico por su ID | |
| const diagnosisNode = doc.getElementById(diagnosisId); | |
| if (diagnosisNode) { | |
| console.log(`[proa.js v12] Nodo para diagnóstico ID '${diagnosisId}' encontrado.`); | |
| const clonedDiagnosisContent = diagnosisNode.cloneNode(true); | |
| // Opcional: Limpiar scripts si los hubiera dentro del div del diagnóstico (poco probable pero seguro) | |
| const scripts = clonedDiagnosisContent.querySelectorAll('script'); | |
| scripts.forEach(script => script.remove()); | |
| if (scripts.length > 0) console.log(`[proa.js v12] Eliminados ${scripts.length} script(s) del nodo de diagnóstico.`); | |
| guideContentDisplay.innerHTML = ''; // Limpiar área | |
| guideContentDisplay.appendChild(clonedDiagnosisContent); // Añadir contenido del diagnóstico | |
| console.log(`[proa.js v12] Contenido del diagnóstico '${diagnosisId}' mostrado.`); | |
| guideContentDisplay.scrollTop = 0; // Ir al inicio | |
| } else { | |
| throw new Error(`No se encontró el elemento con ID '${diagnosisId}' dentro de '${guideData.file}'. Verifica la estructura del HTML de la guía.`); | |
| } | |
| } catch (error) { | |
| console.error(`[proa.js v12] Error al cargar/mostrar diagnóstico '${diagnosisId}' de ${guideData.file}:`, error); | |
| guideContentDisplay.innerHTML = `<div class="text-center py-20 text-red-600">Error al cargar diagnóstico: ${error.message}</div>`; | |
| // Podríamos querer limpiar el cache si falla la carga | |
| // currentGuideHTMLContent = null; | |
| } | |
| } | |
| // ====================================================================== | |
| // BLOCK END: Content Loading Functions | |
| // ====================================================================== | |
| // ====================================================================== | |
| // BLOCK START: Event Listeners Setup | |
| // ====================================================================== | |
| // --- Listener para el selector PRINCIPAL de Guías --- | |
| guideSelector.addEventListener('change', (event) => { | |
| const selectedOption = event.target.selectedOptions[0]; | |
| const guideId = selectedOption.value; | |
| console.log(`[proa.js v12] Cambió la guía principal seleccionada. ID: ${guideId}`); | |
| // Limpiar UI secundaria y cache de contenido | |
| resetUI(false); // No resetea el selector principal | |
| if (!guideId) { | |
| // Si se seleccionó la opción placeholder "-- Seleccione Guía --" | |
| return; // No hacer nada más | |
| } | |
| // Encontrar los datos completos de la guía seleccionada | |
| currentSelectedGuideData = allGuides.find(g => g.id === guideId); | |
| if (!currentSelectedGuideData) { | |
| console.error(`[proa.js v12] No se encontraron datos para la guía con ID: ${guideId}`); | |
| guideContentDisplay.innerHTML = '<p class="text-red-500 text-center py-10">Error: Datos de guía no encontrados.</p>'; | |
| return; | |
| } | |
| // Decidir si mostrar selector de diagnóstico o cargar contenido completo | |
| if (currentSelectedGuideData.hasDiagnoses === true) { | |
| console.log(`[proa.js v12] La guía '${currentSelectedGuideData.title}' tiene diagnósticos. Poblando selector secundario.`); | |
| populateDiagnosisSelector(currentSelectedGuideData); | |
| // Mostrar un mensaje inicial diferente en el área de contenido | |
| guideContentDisplay.innerHTML = '<div class="text-center py-16 text-gray-400"><i class="fas fa-stethoscope text-5xl mb-4"></i><p>Selecciona un diagnóstico específico del desplegable superior.</p></div>'; | |
| } else { | |
| console.log(`[proa.js v12] La guía '${currentSelectedGuideData.title}' no tiene diagnósticos. Cargando contenido completo.`); | |
| diagnosisSelectorContainer.classList.add('hidden'); // Asegurarse que el selector de diagnóstico esté oculto | |
| loadFullGuideContent(currentSelectedGuideData.file); | |
| } | |
| }); | |
| // --- Listener para el selector SECUNDARIO de Diagnósticos --- | |
| diagnosisSelector.addEventListener('change', (event) => { | |
| const diagnosisId = event.target.value; | |
| console.log(`[proa.js v12] Cambió el diagnóstico seleccionado. ID: ${diagnosisId}`); | |
| if (!diagnosisId) { | |
| // Si se seleccionó "-- Seleccione Diagnóstico --" | |
| guideContentDisplay.innerHTML = '<div class="text-center py-16 text-gray-400"><i class="fas fa-stethoscope text-5xl mb-4"></i><p>Selecciona un diagnóstico específico del desplegable superior.</p></div>'; | |
| return; | |
| } | |
| if (currentSelectedGuideData && currentSelectedGuideData.hasDiagnoses) { | |
| loadAndDisplayDiagnosisContent(currentSelectedGuideData, diagnosisId); | |
| } else { | |
| console.error("[proa.js v12] Se intentó cargar un diagnóstico pero no hay guía con diagnósticos seleccionada."); | |
| guideContentDisplay.innerHTML = '<p class="text-red-500 text-center py-10">Error: Guía base no seleccionada correctamente.</p>'; | |
| } | |
| }); | |
| // --- Listener para el checkbox Pediátrico/Adulto --- | |
| togglePediatricCheckbox.addEventListener('change', () => { | |
| console.log('[proa.js v12] Cambiado filtro pediátrico.'); | |
| // Repoblar el selector principal y resetear completamente la UI | |
| populateGuideSelector(); | |
| // resetUI() ya se llama dentro de populateGuideSelector, así que no hace falta llamarlo de nuevo explícitamente aquí. | |
| }); | |
| console.log("[proa.js v12] Listeners añadidos."); | |
| // ====================================================================== | |
| // BLOCK END: Event Listeners Setup | |
| // ====================================================================== | |
| // ====================================================================== | |
| // BLOCK START: Initial Execution | |
| // ====================================================================== | |
| loadGuidesData(); // Cargar JSON e iniciar UI | |
| // ====================================================================== | |
| // BLOCK END: Initial Execution | |
| // ====================================================================== | |
| }); // Fin DOMContentLoaded | |
| console.log("[proa.js v12] Script completamente definido."); |