Proa / js /proa.js
aarnal80's picture
Update js/proa.js
8b295ca verified
// js/proa.js - Lógica para el visor de Guías PROA (v14 - Rutas corregidas)
// ======================================================================
// BLOCK START: DOM Elements Selection & Initial Setup
// ======================================================================
console.log("[proa.js v14] Script cargado. Esperando DOMContentLoaded...");
window.addEventListener('DOMContentLoaded', () => {
console.log("[proa.js v14] 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');
// Ruta al JSON
const guidesDataUrl = './data/guides_data.json'; // Mantenemos ./ aquí, ya que funciona
// Verificar elementos críticos
if (!guideSelector || !diagnosisSelectorContainer || !diagnosisSelector || !guideContentDisplay || !togglePediatricCheckbox) {
console.error("[proa.js v14] 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 v14] Elementos DOM encontrados.");
// ======================================================================
// BLOCK END: DOM Elements Selection & Initial Setup
// ======================================================================
// ======================================================================
// BLOCK START: State Variables
// ======================================================================
let allGuides = [];
let currentSelectedGuideData = null;
let currentGuideHTMLContent = null;
// ======================================================================
// BLOCK END: State Variables
// ======================================================================
// ======================================================================
// BLOCK START: Function to Fetch Guides Data (from JSON)
// ======================================================================
async function loadGuidesData() {
console.log(`[proa.js v14] Iniciando loadGuidesData desde: ${guidesDataUrl}`);
guideSelector.innerHTML = '<option value="">Cargando...</option>';
guideSelector.disabled = true;
resetUI();
try {
console.log("[proa.js v14] Realizando fetch...");
const response = await fetch(guidesDataUrl);
console.log(`[proa.js v14] 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 v14] Parseando JSON...");
const rawData = await response.json();
console.log("[proa.js v14] JSON parseado.");
if (!Array.isArray(rawData)) throw new Error("El formato del JSON no es un array válido.");
allGuides = rawData.filter(g => g.id && g.title && g.file && typeof g.isPediatric === 'boolean');
console.log(`[proa.js v14] Guías válidas iniciales: ${allGuides.length}`);
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 v14] 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 };
}
}
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.");
allGuides.sort((a, b) => a.title.localeCompare(b.title));
console.log("[proa.js v14] Llamando a populateGuideSelector (mostrará Adultos por defecto)...");
populateGuideSelector();
guideSelector.disabled = false;
console.log("[proa.js v14] Carga inicial de datos completada.");
} catch (error) {
console.error("[proa.js v14] 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
// ======================================================================
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>';
diagnosisSelectorContainer.classList.add('hidden');
currentSelectedGuideData = null;
currentGuideHTMLContent = null;
if(fullReset && guideSelector.options.length > 1) {
guideSelector.value = "";
}
}
function populateGuideSelector() {
const showOnlyPediatric = togglePediatricCheckbox.checked;
console.log(`[proa.js v14] Poblando selector principal. Mostrar Pediátricas: ${showOnlyPediatric}`);
const filteredGuides = allGuides.filter(guide => guide.isPediatric === showOnlyPediatric);
filteredGuides.sort((a, b) => a.title.localeCompare(b.title));
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;
option.textContent = guide.isPediatric ? `${guide.title} (PED)` : guide.title;
option.dataset.hasDiagnoses = guide.hasDiagnoses || false;
option.dataset.file = guide.file;
fragment.appendChild(option);
});
guideSelector.appendChild(fragment);
console.log(`[proa.js v14] 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 v14] No se encontraron guías para el filtro actual.`);
}
resetUI(false);
}
function populateDiagnosisSelector(guideData) {
console.log(`[proa.js v14] Poblando selector de diagnósticos para: ${guideData.title}`);
diagnosisSelector.innerHTML = '<option value="">-- Seleccione Diagnóstico --</option>';
if (guideData.hasDiagnoses && Array.isArray(guideData.diagnoses)) {
const fragment = document.createDocumentFragment();
guideData.diagnoses.forEach(diagnosis => {
const option = document.createElement('option');
option.value = diagnosis.id;
option.textContent = diagnosis.title;
fragment.appendChild(option);
});
diagnosisSelector.appendChild(fragment);
diagnosisSelectorContainer.classList.remove('hidden');
console.log(`[proa.js v14] Selector de diagnósticos poblado con ${guideData.diagnoses.length} opciones.`);
} else {
console.warn("[proa.js v14] Se intentó poblar diagnósticos para una guía sin ellos o con datos inválidos.");
diagnosisSelectorContainer.classList.add('hidden');
}
}
// ======================================================================
// BLOCK END: UI Update Functions
// ======================================================================
// ======================================================================
// BLOCK START: Content Loading Functions
// ======================================================================
async function loadFullGuideContent(guideFile) {
if (!guideFile) {
resetUI();
return;
}
// ! RUTA CORREGIDA: Sin ./ !
const guideUrl = guideFile; // Asume que la ruta en JSON (ej. "guias/archivo.html") es relativa a la raíz
console.log(`[proa.js v14] Solicitando contenido COMPLETO de: ${guideUrl}`);
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;
try {
const response = await fetch(guideUrl);
if (!response.ok) throw new Error(`Error HTTP ${response.status} al cargar ${guideUrl}`);
const htmlText = await response.text();
const parser = new DOMParser();
const doc = parser.parseFromString(htmlText, 'text/html');
// Intenta buscar el primer div con clase treatment-card como contenedor principal
const contentNode = doc.querySelector('.treatment-card') || doc.body;
if (contentNode) {
const clonedContent = contentNode.cloneNode(true);
const scripts = clonedContent.querySelectorAll('script');
scripts.forEach(script => script.remove());
if (scripts.length > 0) console.log(`[proa.js v14] Eliminados ${scripts.length} script(s) de ${guideUrl}.`);
guideContentDisplay.innerHTML = '';
guideContentDisplay.appendChild(clonedContent);
console.log(`[proa.js v14] Contenido COMPLETO de ${guideUrl} mostrado.`);
guideContentDisplay.scrollTop = 0;
} else {
// Si no encuentra .treatment-card, intenta mostrar el body entero (menos scripts)
const bodyNode = doc.body;
if(bodyNode){
const clonedBody = bodyNode.cloneNode(true);
const scripts = clonedBody.querySelectorAll('script');
scripts.forEach(script => script.remove());
guideContentDisplay.innerHTML = '';
guideContentDisplay.appendChild(clonedBody);
console.warn(`[proa.js v14] No se encontró .treatment-card en '${guideUrl}'. Mostrando contenido del body.`);
guideContentDisplay.scrollTop = 0;
} else {
throw new Error(`No se encontró nodo de contenido principal (ni .treatment-card ni body) en '${guideUrl}'.`);
}
}
} catch (error) {
console.error(`[proa.js v14] Error al cargar/mostrar contenido COMPLETO de ${guideUrl}:`, error);
guideContentDisplay.innerHTML = `<div class="text-center py-20 text-red-600">Error al cargar contenido: ${error.message}</div>`;
}
}
async function loadAndDisplayDiagnosisContent(guideData, diagnosisId) {
// ! RUTA CORREGIDA: Sin ./ !
const guideFileUrl = guideData.file; // Asume que la ruta en JSON es relativa a la raíz
console.log(`[proa.js v14] Solicitando diagnóstico '${diagnosisId}' de la guía '${guideData.title}' (${guideFileUrl})`);
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 {
if (!currentGuideHTMLContent) {
console.log(`[proa.js v14] HTML de ${guideFileUrl} no cacheado. Realizando fetch...`);
const response = await fetch(guideFileUrl);
if (!response.ok) throw new Error(`Error HTTP ${response.status} al cargar ${guideFileUrl}`);
currentGuideHTMLContent = await response.text();
console.log(`[proa.js v14] HTML de ${guideFileUrl} cargado y cacheado.`);
} else {
console.log(`[proa.js v14] Usando HTML cacheado de ${guideFileUrl}.`);
}
const parser = new DOMParser();
const doc = parser.parseFromString(currentGuideHTMLContent, 'text/html');
const diagnosisNode = doc.getElementById(diagnosisId);
if (diagnosisNode) {
console.log(`[proa.js v14] Nodo para diagnóstico ID '${diagnosisId}' encontrado.`);
const clonedDiagnosisContent = diagnosisNode.cloneNode(true);
const scripts = clonedDiagnosisContent.querySelectorAll('script');
scripts.forEach(script => script.remove());
if (scripts.length > 0) console.log(`[proa.js v14] Eliminados ${scripts.length} script(s) del nodo de diagnóstico.`);
guideContentDisplay.innerHTML = '';
guideContentDisplay.appendChild(clonedDiagnosisContent);
console.log(`[proa.js v14] Contenido del diagnóstico '${diagnosisId}' mostrado.`);
guideContentDisplay.scrollTop = 0;
} else {
throw new Error(`No se encontró el elemento con ID '${diagnosisId}' dentro de '${guideFileUrl}'. Verifica la estructura del HTML de la guía.`);
}
} catch (error) {
console.error(`[proa.js v14] Error al cargar/mostrar diagnóstico '${diagnosisId}' de ${guideFileUrl}:`, error);
guideContentDisplay.innerHTML = `<div class="text-center py-20 text-red-600">Error al cargar diagnóstico: ${error.message}</div>`;
}
}
// ======================================================================
// BLOCK END: Content Loading Functions
// ======================================================================
// ======================================================================
// BLOCK START: Event Listeners Setup
// ======================================================================
guideSelector.addEventListener('change', (event) => {
const selectedOption = event.target.selectedOptions[0];
const guideId = selectedOption.value;
console.log(`[proa.js v14] Cambió la guía principal seleccionada. ID: ${guideId}`);
resetUI(false);
if (!guideId) {
return;
}
currentSelectedGuideData = allGuides.find(g => g.id === guideId);
if (!currentSelectedGuideData) {
console.error(`[proa.js v14] 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;
}
if (currentSelectedGuideData.hasDiagnoses === true) {
console.log(`[proa.js v14] La guía '${currentSelectedGuideData.title}' tiene diagnósticos. Poblando selector secundario.`);
populateDiagnosisSelector(currentSelectedGuideData);
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 v14] La guía '${currentSelectedGuideData.title}' no tiene diagnósticos. Cargando contenido completo.`);
diagnosisSelectorContainer.classList.add('hidden');
loadFullGuideContent(currentSelectedGuideData.file);
}
});
diagnosisSelector.addEventListener('change', (event) => {
const diagnosisId = event.target.value;
console.log(`[proa.js v14] Cambió el diagnóstico seleccionado. ID: ${diagnosisId}`);
if (!diagnosisId) {
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 v14] 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>';
}
});
togglePediatricCheckbox.addEventListener('change', () => {
console.log('[proa.js v14] Cambiado filtro pediátrico.');
populateGuideSelector();
});
console.log("[proa.js v14] Listeners añadidos.");
// ======================================================================
// BLOCK END: Event Listeners Setup
// ======================================================================
// ======================================================================
// BLOCK START: Initial Execution
// ======================================================================
loadGuidesData();
// ======================================================================
// BLOCK END: Initial Execution
// ======================================================================
}); // Fin DOMContentLoaded
console.log("[proa.js v14] Script completamente definido.");