AppConsulta360 / js /proa.js
aarnal80's picture
Update js/proa.js
e08f51d verified
// 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.");