🩺

PediSalud

Dr. Martín Medina Moncada

Pediatría y Neumología Pediátrica

PediSalud v1.0 © 2026

`; const w = window.open('','_blank'); w.document.write(html); w.document.close(); w.print(); } function checkSignosIA() { const temp = parseFloat(document.getElementById('ia-temp').value); const fc = parseFloat(document.getElementById('ia-fc').value); const fr = parseFloat(document.getElementById('ia-fr').value); const spo2 = parseFloat(document.getElementById('ia-spo2').value); const pas = parseFloat(document.getElementById('ia-pas').value); const edad = parseFloat(document.getElementById('ia-edad-umb').value) || 5; const res = document.getElementById('ia-umbrales-result'); const alertas = []; const ok = []; if (!isNaN(temp)) { if (temp >= 40) alertas.push({ s: '🌡 Temperatura', v: temp + '°C', msg: 'FIEBRE ALTA — Evaluar urgente', color: '#ef4444' }); else if (temp >= 38) alertas.push({ s: '🌡 Temperatura', v: temp + '°C', msg: 'Fiebre — Tratar y monitorear', color: '#f97316' }); else if (temp < 36) alertas.push({ s: '🌡 Temperatura', v: temp + '°C', msg: 'Hipotermia — Evaluar causa', color: '#3b82f6' }); else ok.push('🌡 Temperatura: ' + temp + '°C (Normal)'); } const fcMin = edad < 1 ? 100 : edad < 3 ? 90 : edad < 6 ? 80 : edad < 12 ? 70 : 60; const fcMax = edad < 1 ? 160 : edad < 3 ? 150 : edad < 6 ? 120 : edad < 12 ? 110 : 100; if (!isNaN(fc)) { if (fc > fcMax) alertas.push({ s: '❤️ FC', v: fc + ' lpm', msg: `Taquicardia (Normal ${fcMin}-${fcMax})`, color: '#f97316' }); else if (fc < fcMin) alertas.push({ s: '❤️ FC', v: fc + ' lpm', msg: `Bradicardia (Normal ${fcMin}-${fcMax})`, color: '#3b82f6' }); else ok.push(`❤️ FC: ${fc} lpm (Normal ${fcMin}-${fcMax})`); } const frMax = edad < 1 ? 60 : edad < 3 ? 40 : edad < 6 ? 34 : edad < 12 ? 30 : 20; if (!isNaN(fr)) { if (fr > frMax) alertas.push({ s: '🫁 FR', v: fr + ' rpm', msg: `Taquipnea (Normal <${frMax})`, color: '#f97316' }); else ok.push(`🫁 FR: ${fr} rpm (Normal <${frMax})`); } if (!isNaN(spo2)) { if (spo2 < 90) alertas.push({ s: '💨 SpO2', v: spo2 + '%', msg: 'HIPOXEMIA GRAVE — Oxígeno urgente', color: '#ef4444' }); else if (spo2 < 94) alertas.push({ s: '💨 SpO2', v: spo2 + '%', msg: 'Hipoxemia moderada — Evaluar O2', color: '#f97316' }); else ok.push('💨 SpO2: ' + spo2 + '% (Normal ≥94%)'); } const pasMin = edad < 1 ? 60 : edad < 3 ? 70 : edad < 6 ? 75 : edad < 12 ? 80 : 90; if (!isNaN(pas)) { if (pas < pasMin) alertas.push({ s: '🩺 PAS', v: pas + ' mmHg', msg: `Hipotensión (Normal >${pasMin})`, color: '#ef4444' }); else ok.push(`🩺 PAS: ${pas} mmHg (Normal >${pasMin})`); } let html = ''; if (alertas.length) { html += alertas.map(a => `
${a.s}: ${a.v}
${a.msg}
`).join(''); } if (ok.length) { html += `
✅ Dentro de rangos normales:
${ok.map(s=>`${s}`).join('
')}
`; } if (!html) html = '
⚠️ Ingresa al menos un signo vital para evaluar.
'; res.innerHTML = html; } function calcPercentilesIA() { const sexo = document.getElementById('ia-sexo').value; const meses = parseFloat(document.getElementById('ia-edad-meses').value); const peso = parseFloat(document.getElementById('ia-peso-perc').value); const talla = parseFloat(document.getElementById('ia-talla-perc').value); const pc = parseFloat(document.getElementById('ia-pc-perc').value); const res = document.getElementById('ia-percentiles-result'); if (isNaN(meses) || meses < 0 || meses > 60) { res.innerHTML = '
⚠️ Ingresa la edad en meses (0-60).
'; return; } const OMS_PESO_M = {0:[2.5,3.3,4.4],3:[4.4,6.0,8.0],6:[5.7,7.9,10.2],9:[6.6,9.2,11.9],12:[7.1,9.6,12.3],18:[8.1,10.9,13.7],24:[9.0,12.2,15.3],36:[10.8,14.3,17.8],48:[12.3,16.3,20.4],60:[13.7,18.3,23.1]}; const OMS_PESO_F = {0:[2.4,3.2,4.2],3:[4.0,5.7,7.5],6:[5.3,7.3,9.3],9:[6.1,8.5,10.9],12:[6.7,9.0,11.5],18:[7.6,10.2,13.0],24:[8.5,11.5,14.8],36:[10.2,13.9,17.7],48:[11.7,16.1,20.7],60:[13.0,18.2,23.4]}; const OMS_TALLA_M = {0:[46.1,49.9,53.7],3:[55.3,61.4,67.5],6:[61.2,67.6,74.0],9:[65.2,72.0,78.8],12:[68.6,75.7,82.8],18:[74.0,82.3,90.6],24:[78.0,87.8,97.6],36:[85.0,96.1,107.2],48:[90.7,103.3,115.9],60:[95.6,110.0,124.4]}; const OMS_TALLA_F = {0:[45.4,49.1,52.9],3:[53.5,59.8,66.1],6:[59.6,65.7,71.8],9:[63.5,70.1,76.7],12:[66.7,74.0,81.3],18:[72.0,80.7,89.4],24:[76.0,86.4,96.8],36:[83.6,95.1,106.6],48:[89.3,102.7,116.1],60:[94.1,109.4,124.7]}; const pesoRef = sexo === 'M' ? OMS_PESO_M : OMS_PESO_F; const tallaRef = sexo === 'M' ? OMS_TALLA_M : OMS_TALLA_F; const keys = Object.keys(pesoRef).map(Number).sort((a,b)=>a-b); let nearestKey = keys.reduce((prev,curr) => Math.abs(curr-meses) < Math.abs(prev-meses) ? curr : prev); const pRef = pesoRef[nearestKey]; const tRef = tallaRef[nearestKey]; function getPercentile(val, ref) { if (val < ref[0]) return 'P97'; } function getColor(p) { if (p === 'P97') return '#ef4444'; return '#22c55e'; } let html = `
Referencia OMS — ${sexo === 'M' ? 'Masculino' : 'Femenino'} ${meses} meses (ref: ${nearestKey}m)
P3: ${pRef[0]}kg / P50: ${pRef[1]}kg / P97: ${pRef[2]}kg
`; if (!isNaN(peso)) { const p = getPercentile(peso, pRef); html += `
⚖️ Peso: ${peso} kg ${p}
`; } if (!isNaN(talla)) { const p = getPercentile(talla, tRef); html += `
📏 Talla: ${talla} cm ${p}
`; } if (!isNaN(pc)) { const pcRef_M = {0:[31.7,34.5,37.3],3:[37.1,41.0,44.9],6:[40.0,44.3,48.6],12:[43.0,46.5,50.0],24:[45.0,49.0,53.0],36:[46.5,50.5,54.5],48:[47.5,51.5,55.5],60:[48.5,52.5,56.5]}; const pcRef_F = {0:[31.2,33.9,36.6],3:[36.1,39.5,42.9],6:[38.9,43.0,47.1],12:[41.5,45.0,48.5],24:[43.5,47.5,51.5],36:[45.0,49.0,53.0],48:[46.0,50.0,54.0],60:[47.0,51.0,55.0]}; const pcRef = sexo === 'M' ? pcRef_M : pcRef_F; const pcKeys = Object.keys(pcRef).map(Number).sort((a,b)=>a-b); const pcNearest = pcKeys.reduce((prev,curr) => Math.abs(curr-meses) < Math.abs(prev-meses) ? curr : prev); const pRef2 = pcRef[pcNearest]; const p = getPercentile(pc, pRef2); html += `
🧠 PC: ${pc} cm ${p}
`; } res.innerHTML = html; } function calcDDX() { const sintoma = document.getElementById('ia-sintoma').value; const edad = document.getElementById('ia-edad-ddx').value; const res = document.getElementById('ia-ddx-result'); if (!sintoma) { res.innerHTML = '
⚠️ Selecciona un síntoma principal.
'; return; } const DDX = { fiebre: { lactante: [{dx:'Sepsis neonatal',p:'Alta',color:'#ef4444'},{dx:'Meningitis bacteriana',p:'Alta',color:'#ef4444'},{dx:'ITU',p:'Media',color:'#f97316'},{dx:'Otitis media aguda',p:'Media',color:'#f97316'},{dx:'Bronquiolitis',p:'Media',color:'#f97316'},{dx:'Roseola infantum',p:'Baja',color:'#22c55e'},{dx:'Vacuna reciente',p:'Baja',color:'#22c55e'}], preescolar: [{dx:'Faringoamigdalitis',p:'Alta',color:'#ef4444'},{dx:'Otitis media aguda',p:'Alta',color:'#ef4444'},{dx:'Neumonía',p:'Media',color:'#f97316'},{dx:'ITU',p:'Media',color:'#f97316'},{dx:'Exantema viral',p:'Baja',color:'#22c55e'},{dx:'Varicela',p:'Baja',color:'#22c55e'},{dx:'Fiebre sin foco',p:'Baja',color:'#22c55e'}], escolar: [{dx:'Faringoamigdalitis estreptocócica',p:'Alta',color:'#ef4444'},{dx:'Influenza',p:'Alta',color:'#ef4444'},{dx:'Neumonía',p:'Media',color:'#f97316'},{dx:'Mononucleosis',p:'Media',color:'#f97316'},{dx:'ITU',p:'Baja',color:'#22c55e'},{dx:'Dengue',p:'Baja',color:'#22c55e'},{dx:'Fiebre tifoidea',p:'Baja',color:'#22c55e'}], adolescente: [{dx:'Influenza',p:'Alta',color:'#ef4444'},{dx:'Mononucleosis infecciosa',p:'Alta',color:'#ef4444'},{dx:'Faringoamigdalitis',p:'Media',color:'#f97316'},{dx:'Dengue',p:'Media',color:'#f97316'},{dx:'Neumonía atípica',p:'Baja',color:'#22c55e'},{dx:'Fiebre tifoidea',p:'Baja',color:'#22c55e'},{dx:'Apendicitis',p:'Baja',color:'#22c55e'}] }, tos: { lactante: [{dx:'Bronquiolitis (VRS)',p:'Alta',color:'#ef4444'},{dx:'Tos ferina',p:'Alta',color:'#ef4444'},{dx:'Neumonía',p:'Media',color:'#f97316'},{dx:'Aspiración cuerpo extraño',p:'Media',color:'#f97316'},{dx:'Laringotraqueítis',p:'Media',color:'#f97316'},{dx:'Reflujo gastroesofágico',p:'Baja',color:'#22c55e'},{dx:'Rinofaringitis viral',p:'Baja',color:'#22c55e'}], preescolar: [{dx:'Laringotraqueítis (croup)',p:'Alta',color:'#ef4444'},{dx:'Asma',p:'Alta',color:'#ef4444'},{dx:'Neumonía',p:'Media',color:'#f97316'},{dx:'Bronquitis',p:'Media',color:'#f97316'},{dx:'Cuerpo extraño',p:'Media',color:'#f97316'},{dx:'Rinofaringitis',p:'Baja',color:'#22c55e'},{dx:'Sinusitis',p:'Baja',color:'#22c55e'}], escolar: [{dx:'Asma',p:'Alta',color:'#ef4444'},{dx:'Neumonía',p:'Alta',color:'#ef4444'},{dx:'Sinusitis',p:'Media',color:'#f97316'},{dx:'Tos ferina',p:'Media',color:'#f97316'},{dx:'Bronquitis',p:'Baja',color:'#22c55e'},{dx:'Rinitis alérgica',p:'Baja',color:'#22c55e'},{dx:'Reflujo',p:'Baja',color:'#22c55e'}], adolescente: [{dx:'Asma',p:'Alta',color:'#ef4444'},{dx:'Neumonía atípica (Mycoplasma)',p:'Alta',color:'#ef4444'},{dx:'Sinusitis',p:'Media',color:'#f97316'},{dx:'Rinitis alérgica',p:'Media',color:'#f97316'},{dx:'Bronquitis',p:'Baja',color:'#22c55e'},{dx:'Reflujo',p:'Baja',color:'#22c55e'},{dx:'Tos psicógena',p:'Baja',color:'#22c55e'}] }, diarrea: { lactante: [{dx:'Gastroenteritis viral (Rotavirus)',p:'Alta',color:'#ef4444'},{dx:'Enterocolitis bacteriana',p:'Alta',color:'#ef4444'},{dx:'Intolerancia proteína leche',p:'Media',color:'#f97316'},{dx:'Sepsis',p:'Media',color:'#f97316'},{dx:'Invaginación intestinal',p:'Media',color:'#f97316'},{dx:'Diarrea osmótica',p:'Baja',color:'#22c55e'},{dx:'Colitis',p:'Baja',color:'#22c55e'}], preescolar: [{dx:'Gastroenteritis viral',p:'Alta',color:'#ef4444'},{dx:'Giardiasis',p:'Alta',color:'#ef4444'},{dx:'Salmonelosis',p:'Media',color:'#f97316'},{dx:'Amebiasis',p:'Media',color:'#f97316'},{dx:'Intolerancia lactosa',p:'Baja',color:'#22c55e'},{dx:'Apendicitis',p:'Baja',color:'#22c55e'},{dx:'Diarrea del viajero',p:'Baja',color:'#22c55e'}], escolar: [{dx:'Gastroenteritis viral',p:'Alta',color:'#ef4444'},{dx:'Giardiasis',p:'Alta',color:'#ef4444'},{dx:'Salmonelosis',p:'Media',color:'#f97316'},{dx:'Amebiasis',p:'Media',color:'#f97316'},{dx:'EII',p:'Baja',color:'#22c55e'},{dx:'Síndrome intestino irritable',p:'Baja',color:'#22c55e'},{dx:'Intolerancia lactosa',p:'Baja',color:'#22c55e'}], adolescente: [{dx:'Gastroenteritis viral',p:'Alta',color:'#ef4444'},{dx:'Giardiasis',p:'Alta',color:'#ef4444'},{dx:'EII (Crohn/CU)',p:'Media',color:'#f97316'},{dx:'Salmonelosis',p:'Media',color:'#f97316'},{dx:'SII',p:'Baja',color:'#22c55e'},{dx:'Intolerancia lactosa',p:'Baja',color:'#22c55e'},{dx:'Diarrea por antibióticos',p:'Baja',color:'#22c55e'}] }, vomito: { lactante: [{dx:'Estenosis pilórica',p:'Alta',color:'#ef4444'},{dx:'Reflujo gastroesofágico',p:'Alta',color:'#ef4444'},{dx:'Invaginación intestinal',p:'Media',color:'#f97316'},{dx:'Gastroenteritis',p:'Media',color:'#f97316'},{dx:'Sepsis',p:'Media',color:'#f97316'},{dx:'Sobrealimentación',p:'Baja',color:'#22c55e'},{dx:'Intolerancia proteína',p:'Baja',color:'#22c55e'}], preescolar: [{dx:'Gastroenteritis viral',p:'Alta',color:'#ef4444'},{dx:'Invaginación intestinal',p:'Alta',color:'#ef4444'},{dx:'Apendicitis',p:'Media',color:'#f97316'},{dx:'Meningitis',p:'Media',color:'#f97316'},{dx:'Vómito cíclico',p:'Baja',color:'#22c55e'},{dx:'Intoxicación',p:'Baja',color:'#22c55e'},{dx:'Reflujo',p:'Baja',color:'#22c55e'}], escolar: [{dx:'Gastroenteritis',p:'Alta',color:'#ef4444'},{dx:'Apendicitis',p:'Alta',color:'#ef4444'},{dx:'Migraña',p:'Media',color:'#f97316'},{dx:'Vómito cíclico',p:'Media',color:'#f97316'},{dx:'Intoxicación alimentaria',p:'Baja',color:'#22c55e'},{dx:'Pancreatitis',p:'Baja',color:'#22c55e'},{dx:'Reflujo',p:'Baja',color:'#22c55e'}], adolescente: [{dx:'Gastroenteritis',p:'Alta',color:'#ef4444'},{dx:'Apendicitis',p:'Alta',color:'#ef4444'},{dx:'Embarazo (♀)',p:'Media',color:'#f97316'},{dx:'Migraña',p:'Media',color:'#f97316'},{dx:'Pancreatitis',p:'Baja',color:'#22c55e'},{dx:'Bulimia',p:'Baja',color:'#22c55e'},{dx:'Intoxicación',p:'Baja',color:'#22c55e'}] }, dolor_abdominal: { lactante: [{dx:'Cólico del lactante',p:'Alta',color:'#ef4444'},{dx:'Invaginación intestinal',p:'Alta',color:'#ef4444'},{dx:'Hernia incarcerada',p:'Media',color:'#f97316'},{dx:'Vólvulo',p:'Media',color:'#f97316'},{dx:'Enterocolitis',p:'Media',color:'#f97316'},{dx:'Estreñimiento',p:'Baja',color:'#22c55e'},{dx:'Reflujo',p:'Baja',color:'#22c55e'}], preescolar: [{dx:'Apendicitis',p:'Alta',color:'#ef4444'},{dx:'Invaginación',p:'Alta',color:'#ef4444'},{dx:'Estreñimiento',p:'Media',color:'#f97316'},{dx:'ITU',p:'Media',color:'#f97316'},{dx:'Adenitis mesentérica',p:'Baja',color:'#22c55e'},{dx:'Hernia',p:'Baja',color:'#22c55e'},{dx:'Dolor funcional',p:'Baja',color:'#22c55e'}], escolar: [{dx:'Apendicitis',p:'Alta',color:'#ef4444'},{dx:'Estreñimiento',p:'Alta',color:'#ef4444'},{dx:'Adenitis mesentérica',p:'Media',color:'#f97316'},{dx:'ITU',p:'Media',color:'#f97316'},{dx:'Dolor abdominal funcional',p:'Baja',color:'#22c55e'},{dx:'Pancreatitis',p:'Baja',color:'#22c55e'},{dx:'Hernia',p:'Baja',color:'#22c55e'}], adolescente: [{dx:'Apendicitis',p:'Alta',color:'#ef4444'},{dx:'EII',p:'Alta',color:'#ef4444'},{dx:'Embarazo ectópico (♀)',p:'Media',color:'#f97316'},{dx:'Quiste ovárico (♀)',p:'Media',color:'#f97316'},{dx:'SII',p:'Baja',color:'#22c55e'},{dx:'Pancreatitis',p:'Baja',color:'#22c55e'},{dx:'Dolor funcional',p:'Baja',color:'#22c55e'}] }, dificultad_respiratoria: { lactante: [{dx:'Bronquiolitis',p:'Alta',color:'#ef4444'},{dx:'Neumonía',p:'Alta',color:'#ef4444'},{dx:'Cardiopatía congénita',p:'Media',color:'#f97316'},{dx:'Aspiración',p:'Media',color:'#f97316'},{dx:'Laringomalacia',p:'Media',color:'#f97316'},{dx:'Sepsis',p:'Baja',color:'#22c55e'},{dx:'Anemia grave',p:'Baja',color:'#22c55e'}], preescolar: [{dx:'Croup (laringotraqueítis)',p:'Alta',color:'#ef4444'},{dx:'Asma',p:'Alta',color:'#ef4444'},{dx:'Neumonía',p:'Media',color:'#f97316'},{dx:'Cuerpo extraño',p:'Media',color:'#f97316'},{dx:'Epiglotitis',p:'Media',color:'#f97316'},{dx:'Bronquitis',p:'Baja',color:'#22c55e'},{dx:'Anafilaxia',p:'Baja',color:'#22c55e'}], escolar: [{dx:'Asma',p:'Alta',color:'#ef4444'},{dx:'Neumonía',p:'Alta',color:'#ef4444'},{dx:'Anafilaxia',p:'Media',color:'#f97316'},{dx:'Derrame pleural',p:'Media',color:'#f97316'},{dx:'Neumotórax',p:'Baja',color:'#22c55e'},{dx:'Miocarditis',p:'Baja',color:'#22c55e'},{dx:'Anemia',p:'Baja',color:'#22c55e'}], adolescente: [{dx:'Asma',p:'Alta',color:'#ef4444'},{dx:'Neumonía',p:'Alta',color:'#ef4444'},{dx:'TEP',p:'Media',color:'#f97316'},{dx:'Neumotórax espontáneo',p:'Media',color:'#f97316'},{dx:'Anafilaxia',p:'Baja',color:'#22c55e'},{dx:'Miocarditis',p:'Baja',color:'#22c55e'},{dx:'Hiperventilación',p:'Baja',color:'#22c55e'}] }, erupcion: { lactante: [{dx:'Dermatitis atópica',p:'Alta',color:'#ef4444'},{dx:'Roseola infantum',p:'Alta',color:'#ef4444'},{dx:'Dermatitis seborreica',p:'Media',color:'#f97316'},{dx:'Candidiasis cutánea',p:'Media',color:'#f97316'},{dx:'Eritema tóxico neonatal',p:'Media',color:'#f97316'},{dx:'Urticaria',p:'Baja',color:'#22c55e'},{dx:'Miliaria',p:'Baja',color:'#22c55e'}], preescolar: [{dx:'Varicela',p:'Alta',color:'#ef4444'},{dx:'Escarlatina',p:'Alta',color:'#ef4444'},{dx:'Exantema viral',p:'Media',color:'#f97316'},{dx:'Urticaria alérgica',p:'Media',color:'#f97316'},{dx:'Impétigo',p:'Media',color:'#f97316'},{dx:'Dermatitis atópica',p:'Baja',color:'#22c55e'},{dx:'Tiña',p:'Baja',color:'#22c55e'},{dx:'Sarampión',p:'Baja',color:'#22c55e'}], escolar: [{dx:'Varicela',p:'Alta',color:'#ef4444'},{dx:'Escarlatina',p:'Alta',color:'#ef4444'},{dx:'Urticaria',p:'Media',color:'#f97316'},{dx:'Tiña corporis',p:'Media',color:'#f97316'},{dx:'Impétigo',p:'Media',color:'#f97316'},{dx:'Psoriasis',p:'Baja',color:'#22c55e'},{dx:'Pitiriasis rosada',p:'Baja',color:'#22c55e'},{dx:'Dermatitis contacto',p:'Baja',color:'#22c55e'}], adolescente: [{dx:'Acné',p:'Alta',color:'#ef4444'},{dx:'Urticaria',p:'Alta',color:'#ef4444'},{dx:'Pitiriasis rosada',p:'Media',color:'#f97316'},{dx:'Psoriasis',p:'Media',color:'#f97316'},{dx:'Dermatitis contacto',p:'Baja',color:'#22c55e'},{dx:'Tiña versicolor',p:'Baja',color:'#22c55e'},{dx:'Eritema multiforme',p:'Baja',color:'#22c55e'},{dx:'Sífilis secundaria',p:'Baja',color:'#22c55e'}] }, convulsion: { lactante: [{dx:'Convulsión febril',p:'Alta',color:'#ef4444'},{dx:'Meningitis/Encefalitis',p:'Alta',color:'#ef4444'},{dx:'Hipoglucemia',p:'Media',color:'#f97316'},{dx:'Hipocalcemia',p:'Media',color:'#f97316'},{dx:'Epilepsia neonatal',p:'Media',color:'#f97316'},{dx:'Hiponatremia',p:'Baja',color:'#22c55e'},{dx:'Intoxicación',p:'Baja',color:'#22c55e'}], preescolar: [{dx:'Convulsión febril',p:'Alta',color:'#ef4444'},{dx:'Epilepsia',p:'Alta',color:'#ef4444'},{dx:'Meningitis',p:'Media',color:'#f97316'},{dx:'Hipoglucemia',p:'Media',color:'#f97316'},{dx:'Intoxicación',p:'Media',color:'#f97316'},{dx:'Trauma craneal',p:'Baja',color:'#22c55e'},{dx:'Hiponatremia',p:'Baja',color:'#22c55e'}], escolar: [{dx:'Epilepsia',p:'Alta',color:'#ef4444'},{dx:'Meningitis/Encefalitis',p:'Alta',color:'#ef4444'},{dx:'Trauma craneal',p:'Media',color:'#f97316'},{dx:'Hipoglucemia',p:'Media',color:'#f97316'},{dx:'Intoxicación',p:'Baja',color:'#22c55e'},{dx:'Tumor SNC',p:'Baja',color:'#22c55e'},{dx:'Síncope convulsivo',p:'Baja',color:'#22c55e'}], adolescente: [{dx:'Epilepsia',p:'Alta',color:'#ef4444'},{dx:'Trauma craneal',p:'Alta',color:'#ef4444'},{dx:'Meningitis',p:'Media',color:'#f97316'},{dx:'Intoxicación/drogas',p:'Media',color:'#f97316'},{dx:'Tumor SNC',p:'Baja',color:'#22c55e'},{dx:'Síncope convulsivo',p:'Baja',color:'#22c55e'},{dx:'Hipoglucemia',p:'Baja',color:'#22c55e'}] }, llanto_irritabilidad: { lactante: [{dx:'Cólico del lactante',p:'Alta',color:'#ef4444'},{dx:'Otitis media aguda',p:'Alta',color:'#ef4444'},{dx:'Invaginación intestinal',p:'Media',color:'#f97316'},{dx:'Hernia incarcerada',p:'Media',color:'#f97316'},{dx:'Fractura/trauma',p:'Media',color:'#f97316'},{dx:'Meningitis',p:'Baja',color:'#22c55e'},{dx:'Abrasión corneal',p:'Baja',color:'#22c55e'}], preescolar: [{dx:'Otitis media',p:'Alta',color:'#ef4444'},{dx:'Faringoamigdalitis',p:'Alta',color:'#ef4444'},{dx:'Trauma',p:'Media',color:'#f97316'},{dx:'ITU',p:'Media',color:'#f97316'},{dx:'Estreñimiento',p:'Baja',color:'#22c55e'},{dx:'Ansiedad separación',p:'Baja',color:'#22c55e'},{dx:'Cuerpo extraño',p:'Baja',color:'#22c55e'}], escolar: [{dx:'Cefalea',p:'Alta',color:'#ef4444'},{dx:'Dolor abdominal',p:'Alta',color:'#ef4444'},{dx:'Bullying/estrés',p:'Media',color:'#f97316'},{dx:'Migraña',p:'Media',color:'#f97316'},{dx:'Anemia',p:'Baja',color:'#22c55e'},{dx:'Trastorno ansioso',p:'Baja',color:'#22c55e'},{dx:'Hipoglucemia',p:'Baja',color:'#22c55e'}], adolescente: [{dx:'Depresión/ansiedad',p:'Alta',color:'#ef4444'},{dx:'Migraña',p:'Alta',color:'#ef4444'},{dx:'Dolor crónico',p:'Media',color:'#f97316'},{dx:'Trastorno bipolar',p:'Media',color:'#f97316'},{dx:'Abuso de sustancias',p:'Baja',color:'#22c55e'},{dx:'Hipotiroidismo',p:'Baja',color:'#22c55e'},{dx:'Anemia',p:'Baja',color:'#22c55e'}] }, cefalea: { lactante: [{dx:'Meningitis',p:'Alta',color:'#ef4444'},{dx:'Hidrocefalia',p:'Alta',color:'#ef4444'},{dx:'Trauma craneal',p:'Media',color:'#f97316'},{dx:'Hipertensión endocraneana',p:'Media',color:'#f97316'},{dx:'Fiebre',p:'Media',color:'#f97316'},{dx:'Otitis',p:'Baja',color:'#22c55e'},{dx:'Sinusitis',p:'Baja',color:'#22c55e'}], preescolar: [{dx:'Meningitis',p:'Alta',color:'#ef4444'},{dx:'Sinusitis',p:'Alta',color:'#ef4444'},{dx:'Trauma craneal',p:'Media',color:'#f97316'},{dx:'Fiebre',p:'Media',color:'#f97316'},{dx:'Migraña',p:'Baja',color:'#22c55e'},{dx:'Hipertensión',p:'Baja',color:'#22c55e'},{dx:'Tumor SNC',p:'Baja',color:'#22c55e'}], escolar: [{dx:'Migraña',p:'Alta',color:'#ef4444'},{dx:'Cefalea tensional',p:'Alta',color:'#ef4444'},{dx:'Sinusitis',p:'Media',color:'#f97316'},{dx:'Meningitis',p:'Media',color:'#f97316'},{dx:'Hipertensión',p:'Baja',color:'#22c55e'},{dx:'Tumor SNC',p:'Baja',color:'#22c55e'},{dx:'Trauma',p:'Baja',color:'#22c55e'}], adolescente: [{dx:'Migraña',p:'Alta',color:'#ef4444'},{dx:'Cefalea tensional',p:'Alta',color:'#ef4444'},{dx:'Sinusitis',p:'Media',color:'#f97316'},{dx:'Hipertensión',p:'Media',color:'#f97316'},{dx:'Meningitis',p:'Baja',color:'#22c55e'},{dx:'Tumor SNC',p:'Baja',color:'#22c55e'},{dx:'Cefalea en racimos',p:'Baja',color:'#22c55e'}] }, dolor_oido: { lactante: [{dx:'Otitis media aguda',p:'Alta',color:'#ef4444'},{dx:'Otitis externa',p:'Alta',color:'#ef4444'},{dx:'Cuerpo extraño',p:'Media',color:'#f97316'},{dx:'Mastoiditis',p:'Media',color:'#f97316'},{dx:'Dolor referido dental',p:'Baja',color:'#22c55e'},{dx:'Trauma',p:'Baja',color:'#22c55e'},{dx:'Disfunción trompa Eustaquio',p:'Baja',color:'#22c55e'}], preescolar: [{dx:'Otitis media aguda',p:'Alta',color:'#ef4444'},{dx:'Otitis externa',p:'Alta',color:'#ef4444'},{dx:'Cuerpo extraño',p:'Media',color:'#f97316'},{dx:'Mastoiditis',p:'Media',color:'#f97316'},{dx:'Faringoamigdalitis',p:'Baja',color:'#22c55e'},{dx:'Disfunción trompa',p:'Baja',color:'#22c55e'},{dx:'Trauma',p:'Baja',color:'#22c55e'}], escolar: [{dx:'Otitis media aguda',p:'Alta',color:'#ef4444'},{dx:'Otitis externa',p:'Alta',color:'#ef4444'},{dx:'Mastoiditis',p:'Media',color:'#f97316'},{dx:'Dolor dental referido',p:'Media',color:'#f97316'},{dx:'ATM',p:'Baja',color:'#22c55e'},{dx:'Colesteatoma',p:'Baja',color:'#22c55e'},{dx:'Trauma',p:'Baja',color:'#22c55e'}], adolescente: [{dx:'Otitis externa',p:'Alta',color:'#ef4444'},{dx:'Otitis media',p:'Alta',color:'#ef4444'},{dx:'ATM',p:'Media',color:'#f97316'},{dx:'Dolor dental referido',p:'Media',color:'#f97316'},{dx:'Colesteatoma',p:'Baja',color:'#22c55e'},{dx:'Mastoiditis',p:'Baja',color:'#22c55e'},{dx:'Neuralgia',p:'Baja',color:'#22c55e'}] }, odinofagia: { lactante: [{dx:'Candidiasis oral',p:'Alta',color:'#ef4444'},{dx:'Herpangina',p:'Alta',color:'#ef4444'},{dx:'Reflujo',p:'Media',color:'#f97316'},{dx:'Faringitis viral',p:'Media',color:'#f97316'},{dx:'Cuerpo extraño',p:'Media',color:'#f97316'},{dx:'Epiglotitis',p:'Baja',color:'#22c55e'},{dx:'Absceso retrofaríngeo',p:'Baja',color:'#22c55e'}], preescolar: [{dx:'Faringoamigdalitis viral',p:'Alta',color:'#ef4444'},{dx:'Herpangina',p:'Alta',color:'#ef4444'},{dx:'Faringoamigdalitis estreptocócica',p:'Media',color:'#f97316'},{dx:'Absceso periamigdalino',p:'Media',color:'#f97316'},{dx:'Epiglotitis',p:'Baja',color:'#22c55e'},{dx:'Cuerpo extraño',p:'Baja',color:'#22c55e'},{dx:'Mononucleosis',p:'Baja',color:'#22c55e'}], escolar: [{dx:'Faringoamigdalitis estreptocócica',p:'Alta',color:'#ef4444'},{dx:'Mononucleosis',p:'Alta',color:'#ef4444'},{dx:'Faringoamigdalitis viral',p:'Media',color:'#f97316'},{dx:'Absceso periamigdalino',p:'Media',color:'#f97316'},{dx:'Reflujo',p:'Baja',color:'#22c55e'},{dx:'Epiglotitis',p:'Baja',color:'#22c55e'},{dx:'Cuerpo extraño',p:'Baja',color:'#22c55e'}], adolescente: [{dx:'Faringoamigdalitis estreptocócica',p:'Alta',color:'#ef4444'},{dx:'Mononucleosis infecciosa',p:'Alta',color:'#ef4444'},{dx:'Absceso periamigdalino',p:'Media',color:'#f97316'},{dx:'Reflujo',p:'Media',color:'#f97316'},{dx:'Epiglotitis',p:'Baja',color:'#22c55e'},{dx:'Cáncer (raro)',p:'Baja',color:'#22c55e'},{dx:'Faringitis gonocócica',p:'Baja',color:'#22c55e'}] }, hematuria: { lactante: [{dx:'ITU',p:'Alta',color:'#ef4444'},{dx:'Nefropatía por IgA',p:'Alta',color:'#ef4444'},{dx:'Trauma renal',p:'Media',color:'#f97316'},{dx:'Malformación renal',p:'Media',color:'#f97316'},{dx:'Coagulopatía',p:'Media',color:'#f97316'},{dx:'Tumor de Wilms',p:'Baja',color:'#22c55e'},{dx:'Hipercalciuria',p:'Baja',color:'#22c55e'}], preescolar: [{dx:'ITU',p:'Alta',color:'#ef4444'},{dx:'Tumor de Wilms',p:'Alta',color:'#ef4444'},{dx:'Glomerulonefritis',p:'Media',color:'#f97316'},{dx:'Trauma',p:'Media',color:'#f97316'},{dx:'Hipercalciuria',p:'Baja',color:'#22c55e'},{dx:'Nefropatía IgA',p:'Baja',color:'#22c55e'},{dx:'Cálculo renal',p:'Baja',color:'#22c55e'}], escolar: [{dx:'Glomerulonefritis post-estreptocócica',p:'Alta',color:'#ef4444'},{dx:'ITU',p:'Alta',color:'#ef4444'},{dx:'Nefropatía IgA',p:'Media',color:'#f97316'},{dx:'Hipercalciuria',p:'Media',color:'#f97316'},{dx:'Trauma',p:'Baja',color:'#22c55e'},{dx:'Cálculo renal',p:'Baja',color:'#22c55e'},{dx:'Tumor',p:'Baja',color:'#22c55e'}], adolescente: [{dx:'ITU',p:'Alta',color:'#ef4444'},{dx:'Glomerulonefritis',p:'Alta',color:'#ef4444'},{dx:'Cálculo renal',p:'Media',color:'#f97316'},{dx:'Nefropatía IgA',p:'Media',color:'#f97316'},{dx:'Trauma',p:'Baja',color:'#22c55e'},{dx:'Lupus nefritis',p:'Baja',color:'#22c55e'},{dx:'Tumor',p:'Baja',color:'#22c55e'}] } }; const lista = DDX[sintoma]?.[edad] || []; if (!lista.length) { res.innerHTML = '
⚠️ No hay datos para esta combinación.
'; return; } const sintomaLabel = document.getElementById('ia-sintoma').options[document.getElementById('ia-sintoma').selectedIndex].text; const edadLabel = document.getElementById('ia-edad-ddx').options[document.getElementById('ia-edad-ddx').selectedIndex].text; let html = `
🔍 DDx: ${sintomaLabel} — ${edadLabel}
`; html += lista.map((item,i) => `
${i+1}
${item.dx}
${item.p}
`).join(''); res.innerHTML = html; } // ============================================================ // WIZARD DE CONSULTA PEDIÁTRICO // ============================================================ let _wizStep = 0; let _wizPat = null; let _wizSignos = {}; let _wizSintomas = ''; let _wizDiag = ''; let _wizPlan = ''; let _wizReceta = []; let _wizFromControlId = null; function renderWizard() { const sec = document.getElementById('sec-wizard'); if (!sec) return; if (_wizStep === 0) _wizPat = null; sec.innerHTML = coreBuildWizard('renderWizard'); } async async function renderWhatsApp() { const main = document.getElementById('mainContent'); const existing = document.getElementById('sec-whatsapp'); if (existing) { existing.style.display = 'block'; pedOwaLoadStatus(); return; } const sec = document.createElement('div'); sec.id = 'sec-whatsapp'; sec.innerHTML = '

WhatsApp

Activacion de sesion - PediSalud

Verificando estado...

'; main.appendChild(sec); pedOwaLoadStatus(); } function pedOwaLoadStatus() { const box = document.getElementById('pedOwaStatusBox'); if (!box) return; fetch('/api/whatsapp/owa-status', { headers: { 'Authorization': 'Bearer ' + TOKEN } }) .then(r => r.json()) .then(d => { const status = d.status || 'unknown'; if (status === 'ready' || status === 'connected' || status === 'authenticated') { clearInterval(_pedWaTimer); box.innerHTML = '

WhatsApp Conectado

Sesion activa y lista para enviar mensajes

Telefono: ' + (d.phone || 'N/A') + ' | Nombre: ' + (d.pushName || '-') + '
'; } else { pedOwaLoadQR(); } }) .catch(() => { const b = document.getElementById('pedOwaStatusBox'); if (b) b.innerHTML = '

Error al conectar con OpenWA

'; }); } function pedOwaLoadQR() { const box = document.getElementById('pedOwaStatusBox'); if (!box) return; fetch('/api/whatsapp/owa-qr', { headers: { 'Authorization': 'Bearer ' + TOKEN } }) .then(r => r.json()) .then(d => { _pedWaCountdown = 15; clearInterval(_pedWaTimer); _pedWaTimer = setInterval(() => { _pedWaCountdown--; const cd = document.getElementById('pedOwaCountdown'); if (cd) cd.textContent = _pedWaCountdown; if (_pedWaCountdown <= 0) { clearInterval(_pedWaTimer); pedOwaLoadStatus(); } }, 1000); box.innerHTML = '
Escanea el QR con WhatsApp para activar

Actualiza en 15s

Como vincular:
1. Abre WhatsApp en el telefono de la clinica
2. Ve a Menu - Dispositivos vinculados
3. Toca Vincular un dispositivo
4. Escanea este QR
'; }) .catch(() => { const b = document.getElementById('pedOwaStatusBox'); if (b) b.innerHTML = '

Error al cargar QR

'; }); } function pedOwaDisconnect() { if (!confirm('Desconectar WhatsApp?')) return; fetch('/api/whatsapp/owa-stop', { method: 'POST', headers: { 'Authorization': 'Bearer ' + TOKEN } }) .then(() => setTimeout(pedOwaLoadStatus, 1500)); } function loadPediWAStatus() { pedOwaLoadStatus(); } function startPediWASession() { pedOwaLoadStatus(); } function restartPediWASession() { pedOwaLoadStatus(); } function disconnectPediWA() { pedOwaDisconnect(); } function toast(msg, type = 'success') { const colors = { success: '#16a34a', error: '#dc2626', info: '#2563eb', warning: '#ca8a04' }; const t = document.createElement('div'); t.style.cssText = `position:fixed;bottom:20px;right:20px;background:${colors[type]};color:#fff; padding:12px 20px;border-radius:10px;font-size:13px;font-weight:600;z-index:9999; box-shadow:0 4px 20px rgba(0,0,0,.3);animation:slideIn .3s ease`; t.textContent = msg; document.body.appendChild(t); setTimeout(() => t.remove(), 3500); } // ============================================================ // UTILIDADES // ============================================================ function calcAge(birthDate) { const b = new Date(birthDate); const now = new Date(); const years = Math.floor((now - b) / (365.25 * 24 * 60 * 60 * 1000)); const months = Math.floor((now - b) / (30.44 * 24 * 60 * 60 * 1000)); if (years >= 2) return `${years} años`; if (months >= 1) return `${months} meses`; const days = Math.floor((now - b) / (24 * 60 * 60 * 1000)); return `${days} días`; } function formatDate(d) { if (!d) return '-'; return new Date(d).toLocaleDateString('es-HN', { day: '2-digit', month: 'short', year: 'numeric' }); } function formatTime(t) { if (!t) return '-'; return t.substring(0, 5); } function statusBadge(status) { const map = { programado: ['badge-blue', '📅 Programado'], confirmado: ['badge-teal', '✅ Confirmado'], en_consulta: ['badge-yellow', '🩺 En consulta'], completado: ['badge-green', '✔ Completado'], cancelado: ['badge-red', '✖ Cancelado'], no_asistio: ['badge-gray', '⚠ No asistió'] }; const [cls, label] = map[status] || ['badge-gray', status]; return `${label}`; } // ============================================================ // SECCIÓN: CITAS (Solicitudes de citas en línea/WhatsApp) // ============================================================ async function loadCitasBadge() { try { const data = await api('GET', '/appointments/pending-count'); const count = data?.count || 0; const badge = document.getElementById('citasBadge'); const statNum = document.getElementById('statCitasNum'); if (badge) { badge.textContent = count; badge.style.display = count > 0 ? 'inline' : 'none'; } if (statNum) statNum.textContent = count; } catch(e) { const statNum = document.getElementById('statCitasNum'); if (statNum) statNum.textContent = '?'; } } async function renderCitas() { const sec = document.getElementById('sec-citas'); sec.innerHTML = '
'; try { const [pendientes, todas] = await Promise.all([ api('GET', '/appointments?status=pendiente&limit=100'), api('GET', '/appointments?limit=50') ]); const serviceMap = { nino_sano: '👶 Niño Sano', vacuna: '💉 Vacuna', enfermedad: '🤒 Enfermedad', primera_vez: '🆕 Primera Vez', seguimiento: '🔄 Seguimiento', otro: '📋 Otro' }; const statusMap = { pendiente: ['#fef3c7','#92400e','⏳ Pendiente'], confirmado: ['#d1fae5','#065f46','✅ Confirmado'], atendido: ['#dbeafe','#1e40af','✔ Atendido'], cancelado: ['#fee2e2','#991b1b','✖ Cancelado'] }; const renderRow = (c) => { const [bg, color, label] = statusMap[c.status] || ['#f3f4f6','#374151', c.status]; return `
👶 ${c.child_name}
👨‍👩‍👦 ${c.parent_name} · 📞 ${c.phone}
${c.child_age ? '
Edad: ' + c.child_age + '
' : ''} ${c.reason ? '
"' + c.reason + '"
' : ''}
${serviceMap[c.service_type] || c.service_type}
${c.preferred_date ? '
📅 ' + new Date(c.preferred_date.includes('T') ? c.preferred_date : c.preferred_date+'T12:00:00').toLocaleDateString('es-HN',{day:'2-digit',month:'short',year:'numeric'}) + (c.preferred_time ? ' · ' + c.preferred_time.substring(0,5) : '') + '
' : ''}
${label}
${(c.created_at ? new Date(c.created_at).toLocaleDateString('es-HN',{day:'2-digit',month:'short'}) : '')}
${c.status === 'pendiente' ? `
` : ''}
`; }; sec.innerHTML = `

📅 Solicitudes de Citas

Citas solicitadas desde el sitio web o WhatsApp

${pendientes && pendientes.length > 0 ? `
⏳ Pendientes de Confirmar (${pendientes.length})
${pendientes.map(renderRow).join('')}
` : `

No hay citas pendientes de confirmar

`}
📋 Historial de Solicitudes
${todas && todas.filter(c => c.status !== 'pendiente').length > 0 ? todas.filter(c => c.status !== 'pendiente').map(renderRow).join('') : '
Sin historial
' }
`; } catch(e) { sec.innerHTML = '
Error: ' + e.message + '
'; } } async function confirmarCita(id) { const [patients, controlTypes] = await Promise.all([ api('GET', '/patients?limit=200'), api('GET', '/controls/types/list') ]); const today = new Date().toISOString().split('T')[0]; openModal(` `); } async function guardarConfirmacion(appointmentId) { const patientId = document.getElementById('confirmPatient').value; const controlTypeId = document.getElementById('confirmControlType').value; const date = document.getElementById('confirmDate').value; const time = document.getElementById('confirmTime').value; const notes = document.getElementById('confirmNotes').value; if (patientId === 'nuevo') { closeModal(); abrirNuevoPaciente(); return; } if (!patientId || !controlTypeId || !date) { toast('Complete todos los campos requeridos', 'error'); return; } try { await api('POST', '/appointments/' + appointmentId + '/convert', { patient_id: parseInt(patientId), control_type_id: parseInt(controlTypeId), scheduled_date: date, scheduled_time: time, notes }); closeModal(); toast('✅ Cita confirmada y programada', 'success'); loadCitasBadge(); renderCitas(); } catch(e) { toast('Error: ' + e.message, 'error'); } } async function rechazarCita(id) { if (!confirm('¿Rechazar esta solicitud de cita?')) return; try { await api('PATCH', '/appointments/' + id + '/status', { status: 'cancelado' }); toast('Solicitud rechazada', 'info'); loadCitasBadge(); renderCitas(); } catch(e) { toast('Error: ' + e.message, 'error'); } } function contactarWA(phone, childName, parentName) { const cleanPhone = phone.replace(/[^0-9]/g, ''); const msg = encodeURIComponent('Hola ' + parentName + ', le contactamos de la Clínica PediSalud del Dr. Medina para confirmar la cita de ' + childName + '. ¿Cuándo le queda bien?'); window.open('https://wa.me/504' + cleanPhone + '?text=' + msg, '_blank'); } // ============================================================ // SECCIÓN: HOY // ============================================================ async function renderHoy() { const sec = document.getElementById('sec-hoy'); sec.innerHTML = '
'; try { const [stats, controls] = await Promise.all([ api('GET', '/controls/stats/dashboard'), api('GET', '/controls/today') ]); const nextControl = controls?.find(c => c.status === 'programado' || c.status === 'confirmado'); sec.innerHTML = `
${stats?.today_controls || 0}
Controles Hoy
${stats?.today_completed || 0}
Completados
${stats?.week_controls || 0}
Esta Semana
${stats?.total_patients || 0}
Pacientes
⏰ Próximo control — ${formatTime(nextControl.scheduled_time)}
${nextControl.first_name} ${nextControl.last_name}
${nextControl.control_type_name || 'Control'} · ${calcAge(nextControl.birth_date)} · ${nextControl.mother_name ? '👩 ' + nextControl.mother_name : ''}
` : ''}
📋 Controles de Hoy
${!controls || controls.length === 0 ? `
📅

No hay controles programados para hoy

` : controls.map(c => `
${(c.first_name && c.last_name) ? c.first_name + ' ' + c.last_name : (c.patient_name || 'Sin nombre')}
${c.control_type_name || 'Control'} · ${c.scheduled_time ? c.scheduled_time.substring(0,5) : (c.scheduled_date ? new Date(c.scheduled_date).toLocaleTimeString('es-HN',{hour:'2-digit',minute:'2-digit'}) : '')}
`).join('')}
`; } catch(e) { sec.innerHTML = `
Error: ${e.message}
`; } } async function abrirNuevoPaciente() { openModal(` `); } async function guardarPaciente() { const fn = document.getElementById('pFirstName').value.trim(); const ln = document.getElementById('pLastName').value.trim(); const bd = document.getElementById('pBirthDate').value; const sx = document.getElementById('pSex').value; if (!fn || !ln || !bd || !sx) { toast('Completa los campos obligatorios', 'error'); return; } try { const data = { first_name: fn, last_name: ln, birth_date: bd, sex: sx, blood_type: document.getElementById('pBloodType').value, mother_name: document.getElementById('pMotherName').value, father_name: document.getElementById('pFatherName').value, phone_primary: document.getElementById('pPhone').value, whatsapp: document.getElementById('pWhatsapp').value, allergies: document.getElementById('pAllergies').value, address: document.getElementById('pAddress').value, insurance_type: document.getElementById('pInsurance').value, insurance_number: document.getElementById('pInsuranceNum')?.value || null, gestational_weeks: document.getElementById('pGestWeeks').value || null, birth_weight_g: document.getElementById('pBirthWeight').value || null, birth_height_cm: document.getElementById('pBirthHeight').value || null, premature: document.getElementById('pPremature').checked }; const result = await api('POST', '/patients', data); toast(`✅ Paciente ${result.expediente} registrado`, 'success'); closeModal(); renderPacientes(); } catch (e) { toast('Error: ' + e.message, 'error'); } } // ============================================================ // CONTROLES - SECCIÓN PRINCIPAL // ============================================================ // ============================================================ // PACIENTES - SECCION PRINCIPAL // ============================================================ async function renderPacientes() { const sec = document.getElementById('sec-pacientes'); sec.innerHTML = '
' + '
' + '\u{1F476} Pacientes' + '' + '
' + '
' + '' + '
' + '
' + '
'; loadPatients(''); } async function loadPatients(search) { const el = document.getElementById('patientsList'); if (!el) return; try { const data = await api('GET', '/patients?search=' + encodeURIComponent(search) + '&limit=50'); if (!data || !data.patients || data.patients.length === 0) { el.innerHTML = '
' + '
\u{1F476}
' + '

No hay pacientes registrados

' + '' + '
'; return; } const rows = data.patients.map(function(p) { const age = calcAge(p.birth_date); const sex = p.sex === 'M' ? '\u2642 Masc' : '\u2640 Fem'; const parent = p.mother_name || p.father_name || '-'; const phone = p.phone_primary || '-'; return '' + '' + (p.expediente || '-') + '' + '' + p.first_name + ' ' + p.last_name + '' + '' + age + '' + '' + sex + '' + '' + parent + '' + '' + phone + '' + '' + ''; }).join(''); el.innerHTML = '
' + '' + '' + rows + '' + '
ExpedienteNombreEdadSexoMadre/PadreTelefonoAcciones
'; } catch(e) { el.innerHTML = '
Error: ' + e.message + '
'; } } function searchPatients(val) { clearTimeout(window._searchTimer); window._searchTimer = setTimeout(function(){ loadPatients(val); }, 400); } // ============================================================ function navControles() { nav('controles'); } // VER PACIENTE - MODAL DE DETALLE // ============================================================ async function verPaciente(id) { const overlay = document.getElementById('modalOverlay'); const box = document.getElementById('modalBox'); overlay.style.display = 'flex'; box.innerHTML = '

Cargando expediente...

'; try { const summary = await api('GET', '/patients/' + id + '/summary'); const p = summary.patient; const lc = summary.last_consult; const pv = summary.pending_vaccines || []; const pe = summary.pending_exams || []; const lg = summary.last_growth; const age = calcAge(p.birth_date); const sex = p.sex === 'M' ? 'Masculino' : 'Femenino'; const bloodType = p.blood_type || 'No registrado'; const insurance = p.insurance_type && p.insurance_type !== 'ninguno' ? p.insurance_type : 'Ninguno'; box.innerHTML = '' + '' + ''; } catch(e) { box.innerHTML = '
Error: ' + e.message + '
' + '
'; } } async function renderControles() { const sec = document.getElementById('sec-controles'); const today = new Date().toISOString().split('T')[0]; sec.innerHTML = `
📋 Controles
`; loadControlesByDate(today); } async function loadControlesByDate(date) { const el = document.getElementById('controlesList'); if (!el) return; try { const data = await api('GET', `/controls?date=${date}&limit=50`); if (!data || data.length === 0) { el.innerHTML = `
📅

No hay controles para esta fecha

`; return; } el.innerHTML = `
${data.map(c => ` `).join('')}
HoraPacienteTipo de ControlDuraciónEstadoAcciones
${formatTime(c.scheduled_time)}
${c.first_name} ${c.last_name}
${c.expediente || ''}
${c.control_type_name || 'Control'} ${c.duration_minutes} min ${statusBadge(c.status)}
${c.status !== 'completado' && c.status !== 'cancelado' ? ` ` : ''}
`; } catch (e) { el.innerHTML = `
Error: ${e.message}
`; } } // ============================================================ // NUEVO CONTROL // ============================================================ async function abrirNuevoControl(patientId = null) { showLoading('Cargando...'); try { const [types, patients] = await Promise.all([ api('GET', '/controls/types/list'), patientId ? api('GET', `/patients/${patientId}`) : null ]); hideLoading(); const today = new Date().toISOString().split('T')[0]; openModal(` `); } catch (e) { hideLoading(); toast('Error: ' + e.message, 'error'); } } function selectControlType(el, id, duration) { document.querySelectorAll('#ctrlTypeGrid .q-opt').forEach(e => e.classList.remove('selected')); el.classList.add('selected'); document.getElementById('ctrlTypeId').value = id; document.getElementById('ctrlDuration').value = duration; } let patientSearchTimer; async function searchPatientForControl(val) { clearTimeout(patientSearchTimer); if (val.length < 2) { document.getElementById('ctrlPatientResults').innerHTML = ''; return; } patientSearchTimer = setTimeout(async () => { const data = await api('GET', `/patients?search=${encodeURIComponent(val)}&limit=5`); const el = document.getElementById('ctrlPatientResults'); if (!el) return; if (!data || data.patients.length === 0) { el.innerHTML = '

Sin resultados

'; return; } el.innerHTML = data.patients.map(p => `
${p.first_name} ${p.last_name} ${p.expediente} · ${calcAge(p.birth_date)}
`).join(''); }, 300); } function selectPatientForControl(id, name, exp) { document.getElementById('ctrlPatientId').value = id; document.getElementById('ctrlPatientSearch').value = `${name} (${exp})`; document.getElementById('ctrlPatientResults').innerHTML = ''; } async function guardarControl() { const pid = document.getElementById('ctrlPatientId').value; const tid = document.getElementById('ctrlTypeId').value; const date = document.getElementById('ctrlDate').value; const time = document.getElementById('ctrlTime').value; if (!pid) { toast('Selecciona un paciente', 'error'); return; } if (!tid) { toast('Selecciona el tipo de control', 'error'); return; } if (!date || !time) { toast('Ingresa fecha y hora', 'error'); return; } try { await api('POST', '/controls', { patient_id: parseInt(pid), control_type_id: parseInt(tid), scheduled_date: date, scheduled_time: time, duration_minutes: parseInt(document.getElementById('ctrlDuration').value), reason: document.getElementById('ctrlReason').value }); toast('✅ Control programado', 'success'); closeModal(); if (currentSection === 'hoy') renderHoy(); else if (currentSection === 'controles') renderControles(); } catch (e) { toast('Error: ' + e.message, 'error'); } } async function cambiarEstadoControl(id, status) { try { await api('PATCH', `/controls/${id}/status`, { status }); toast('Estado actualizado', 'success'); if (currentSection === 'hoy') renderHoy(); else renderControles(); } catch (e) { toast('Error: ' + e.message, 'error'); } } // ============================================================ async function renderRecetas() { const sec = document.getElementById('sec-recetas'); sec.innerHTML = `
💊 Recetas
Selecciona un paciente para ver sus recetas o crea una nueva.
`; } let recetaSearchTimer; async function searchPatientRecetas(val) { clearTimeout(recetaSearchTimer); if (val.length < 2) { document.getElementById('recetasList').innerHTML = ''; return; } recetaSearchTimer = setTimeout(async () => { const data = await api('GET', `/patients?search=${encodeURIComponent(val)}&limit=5`); const el = document.getElementById('recetasList'); if (!el || !data) return; el.innerHTML = data.patients.map(p => `
${p.sex==='M'?'👦':'👧'}
${p.first_name} ${p.last_name}
${p.expediente} · ${calcAge(p.birth_date)}
`).join(''); }, 300); } async function loadPatientRecetas(pid, name) { document.getElementById('recetaSearch').value = name; document.getElementById('recetasList').innerHTML = '
'; const data = await api('GET', `/prescriptions/patient/${pid}`); const el = document.getElementById('recetasList'); if (!data || data.length === 0) { el.innerHTML = `

Sin recetas registradas

`; return; } el.innerHTML = `
${data.map(r => `
Receta del ${formatDate(r.prescription_date)} ${r.doctor_name || ''}
${(r.medications || []).map(m => `
${m.nombre} — ${m.presentacion}
${m.dosis} · ${m.instrucciones || ''}
`).join('')} ${r.general_instructions ? `

📝 ${r.general_instructions}

` : ''}
`).join('')} `; } async function abrirNuevaReceta(patientId = null, consultId = null) { let patientInfo = null; if (patientId) { patientInfo = await api('GET', `/patients/${patientId}`); } window._rxMeds = []; openModal(` `); renderRxMeds(); } function recalcularDosis() { // Recalcular todas las dosis cuando cambia el peso const weightEl = document.getElementById('rxPatientWeight'); const peso = weightEl ? parseFloat(weightEl.value) : 0; if (!peso || !window._rxMeds) return; // Buscar cada med en MEDS_PED y recalcular window._rxMeds = window._rxMeds.map(med => { const template = MEDS_PED.find(m => m.n === med.nombre); if (template && template.mgkg && template.conc && peso > 0) { const mgDosis = Math.round(template.mgkg * peso * 10) / 10; const ml = Math.round((mgDosis / template.conc) * 10) / 10; return { ...med, dosis: `${mgDosis} mg (${ml} ml)`, cantidad: `${ml} ml` }; } return med; }); renderRxMeds(); } function addMedToRx(idx) { const m = MEDS_PED[idx]; if (!m) return; window._rxMeds = window._rxMeds || []; // Calcular dosis automática si hay peso del paciente const weightEl = document.getElementById('rxPatientWeight'); const peso = weightEl ? parseFloat(weightEl.value) : 0; let dosisCalculada = ''; let mlCalculado = ''; if (peso > 0 && m.mgkg && m.conc) { const mgDosis = Math.round(m.mgkg * peso * 10) / 10; const ml = Math.round((mgDosis / m.conc) * 10) / 10; dosisCalculada = `${mgDosis} mg (${ml} ml)`; mlCalculado = `${ml} ml`; } else if (m.mgkg && m.conc) { dosisCalculada = `${m.mgkg} mg/kg → calcular según peso`; } else { dosisCalculada = m.nota || 'Ver indicación'; } window._rxMeds.push({ nombre: m.n, presentacion: m.p, dosis: dosisCalculada, frecuencia: m.freq, duracion: m.dur, instrucciones: '', cantidad: mlCalculado }); renderRxMeds(); } function renderRxMeds() { const el = document.getElementById('rxMedsList'); if (!el) return; if (!window._rxMeds || window._rxMeds.length === 0) { el.innerHTML = '

Selecciona medicamentos de la lista o agrega uno personalizado.

'; return; } el.innerHTML = window._rxMeds.map((m, i) => `
${m.nombre} ${m.presentacion}
`).join(''); } function removeMed(i) { window._rxMeds.splice(i, 1); renderRxMeds(); } async function guardarReceta(consultId) { const pid = document.getElementById('rxPatientId').value; if (!pid) { toast('Selecciona un paciente', 'error'); return; } if (!window._rxMeds || window._rxMeds.length === 0) { toast('Agrega al menos un medicamento', 'error'); return; } try { await api('POST', '/prescriptions', { patient_id: parseInt(pid), consult_id: consultId, medications: window._rxMeds, general_instructions: document.getElementById('rxInstructions').value }); toast('✅ Receta guardada', 'success'); closeModal(); } catch (e) { toast('Error: ' + e.message, 'error'); } } // ============================================================ // EXÁMENES // ============================================================ async function renderExamenes() { const sec = document.getElementById('sec-examenes'); sec.innerHTML = `
🔬 Exámenes
`; loadPendingExams(); } function switchExamTab(btn, tab) { document.querySelectorAll('#sec-examenes .tab-btn').forEach(b => b.classList.remove('active')); btn.classList.add('active'); document.getElementById('examPendientes').style.display = tab === 'pendientes' ? 'block' : 'none'; document.getElementById('examBuscar').style.display = tab === 'buscar' ? 'block' : 'none'; } async function loadPendingExams() { const el = document.getElementById('examPendientes'); if (!el) return; try { const data = await api('GET', '/exams/pending/list'); if (!data || data.length === 0) { el.innerHTML = '

No hay exámenes pendientes

'; return; } el.innerHTML = `
${data.map(e => ` `).join('')}
PacienteExamenTipoUrgenciaFechaAcciones
${e.first_name} ${e.last_name}
${e.expediente}
${e.exam_name} ${e.exam_type} ${e.urgency} ${formatDate(e.request_date)}
`; } catch (e) { el.innerHTML = `
Error: ${e.message}
`; } } async function abrirNuevoExamen(patientId = null) { openModal(` `); } async function guardarExamen() { const pid = document.getElementById('exPatientId').value; const name = document.getElementById('exName').value.trim(); if (!pid) { toast('Selecciona un paciente', 'error'); return; } if (!name) { toast('Ingresa el nombre del examen', 'error'); return; } try { await api('POST', '/exams', { patient_id: parseInt(pid), exam_type: document.getElementById('exType').value, exam_name: name, urgency: document.getElementById('exUrgency').value, instructions: document.getElementById('exInstructions').value }); toast('✅ Examen solicitado', 'success'); closeModal(); renderExamenes(); } catch (e) { toast('Error: ' + e.message, 'error'); } } async function registrarResultado(examId) { openModal(` `); } async function guardarResultado(examId) { try { await api('PUT', `/exams/${examId}/result`, { result_date: document.getElementById('resDate').value, result_value: document.getElementById('resValue').value, result_interpretation: document.getElementById('resInterp').value, result_notes: document.getElementById('resNotes').value }); toast('✅ Resultado registrado', 'success'); closeModal(); renderExamenes(); } catch (e) { toast('Error: ' + e.message, 'error'); } } // ============================================================ // CURVAS DE CRECIMIENTO OMS // ============================================================ const WHO_WEIGHT_M = { p3: [2.5,3.4,4.3,5.0,5.6,6.1,6.4,6.7,6.9,7.1,7.4,7.6,7.8,8.0,8.2,8.4,8.6,8.7,8.9,9.1,9.2,9.4,9.5,9.7,9.8,10.0,10.1,10.3,10.4,10.5,10.7,10.8,10.9,11.0,11.2,11.3,11.4,11.5,11.6,11.7,11.8,11.9,12.0,12.1,12.2,12.3,12.4,12.5,12.6,12.7,12.8,12.9,13.0,13.1,13.2,13.3,13.4,13.5,13.6,13.7,13.8], p50: [3.3,4.5,5.6,6.4,7.0,7.5,7.9,8.3,8.6,8.9,9.2,9.4,9.6,9.9,10.1,10.3,10.5,10.7,10.9,11.1,11.3,11.5,11.8,12.0,12.2,12.4,12.5,12.7,12.9,13.1,13.3,13.5,13.7,13.8,14.0,14.2,14.3,14.5,14.7,14.8,15.0,15.2,15.3,15.5,15.7,15.8,16.0,16.2,16.3,16.5,16.6,16.8,17.0,17.1,17.3,17.5,17.6,17.8,18.0,18.1,18.3], p97: [4.4,5.8,7.1,8.0,8.7,9.3,9.8,10.3,10.7,11.0,11.4,11.7,12.0,12.3,12.6,12.9,13.2,13.5,13.7,14.0,14.2,14.5,14.8,15.1,15.4,15.7,15.9,16.2,16.5,16.8,17.1,17.4,17.7,18.0,18.3,18.6,18.9,19.2,19.5,19.8,20.1,20.4,20.7,21.0,21.3,21.6,21.9,22.2,22.5,22.8,23.1,23.4,23.7,24.0,24.3,24.6,24.9,25.2,25.5,25.8,26.1] }; const WHO_WEIGHT_F = { p3: [2.4,3.2,4.0,4.7,5.2,5.7,6.0,6.3,6.5,6.7,6.9,7.1,7.3,7.5,7.7,7.9,8.1,8.3,8.4,8.6,8.7,8.9,9.0,9.2,9.3,9.5,9.6,9.8,9.9,10.0,10.2,10.3,10.4,10.5,10.7,10.8,10.9,11.0,11.1,11.2,11.3,11.4,11.5,11.6,11.7,11.8,11.9,12.0,12.1,12.2,12.3,12.4,12.5,12.6,12.7,12.8,12.9,13.0,13.1,13.2,13.3], p50: [3.2,4.2,5.1,5.8,6.4,6.9,7.3,7.6,7.9,8.2,8.5,8.7,8.9,9.2,9.4,9.6,9.8,10.0,10.2,10.4,10.6,10.9,11.1,11.3,11.5,11.7,11.9,12.1,12.3,12.5,12.7,12.9,13.1,13.3,13.5,13.7,13.9,14.1,14.3,14.5,14.7,14.9,15.1,15.3,15.5,15.7,15.9,16.1,16.3,16.5,16.7,16.9,17.1,17.3,17.5,17.7,17.9,18.1,18.3,18.5,18.7], p97: [4.2,5.5,6.9,7.7,8.5,9.1,9.6,10.1,10.5,10.9,11.2,11.5,11.8,12.1,12.4,12.7,13.0,13.3,13.6,13.9,14.2,14.5,14.8,15.1,15.4,15.7,16.0,16.3,16.6,16.9,17.2,17.5,17.8,18.1,18.4,18.7,19.0,19.3,19.6,19.9,20.2,20.5,20.8,21.1,21.4,21.7,22.0,22.3,22.6,22.9,23.2,23.5,23.8,24.1,24.4,24.7,25.0,25.3,25.6,25.9,26.2] }; const WHO_HEIGHT_M = { p3: [46.1,50.8,54.4,57.3,59.7,61.7,63.3,64.8,66.2,67.5,68.7,69.9,71.0,72.1,73.1,74.1,75.0,76.0,76.9,77.7,78.6,79.4,80.2,81.0,81.7,82.5,83.2,83.9,84.6,85.3,86.0,86.7,87.3,88.0,88.6,89.2,89.9,90.5,91.1,91.7,92.3,92.9,93.4,94.0,94.6,95.1,95.7,96.2,96.7,97.3,97.8,98.3,98.8,99.3,99.8,100.3,100.8,101.3,101.8,102.3,102.8], p50: [49.9,54.7,58.4,61.4,63.9,65.9,67.6,69.2,70.6,72.0,73.3,74.5,75.7,76.9,78.0,79.1,80.2,81.2,82.3,83.2,84.2,85.1,86.0,86.9,87.8,88.7,89.5,90.4,91.2,92.1,92.9,93.7,94.5,95.4,96.1,96.9,97.7,98.5,99.2,100.0,100.7,101.5,102.2,102.9,103.7,104.4,105.1,105.8,106.5,107.2,107.9,108.6,109.3,110.0,110.7,111.3,112.0,112.7,113.4,114.0,114.7], p97: [53.7,58.6,62.4,65.5,68.0,70.1,71.9,73.5,75.0,76.5,77.9,79.2,80.5,81.8,83.0,84.2,85.4,86.5,87.7,88.8,89.9,91.0,92.1,93.1,94.2,95.2,96.2,97.2,98.2,99.2,100.2,101.1,102.1,103.0,104.0,104.9,105.8,106.7,107.6,108.5,109.4,110.3,111.2,112.1,113.0,113.8,114.7,115.6,116.4,117.3,118.1,119.0,119.8,120.7,121.5,122.3,123.2,124.0,124.8,125.7,126.5] }; const WHO_HEIGHT_F = { p3: [45.6,50.0,53.2,55.8,58.0,59.9,61.5,62.9,64.3,65.6,66.8,68.0,69.0,70.1,71.1,72.0,73.0,73.9,74.8,75.6,76.5,77.3,78.1,78.9,79.7,80.5,81.2,82.0,82.7,83.4,84.2,84.9,85.6,86.3,87.0,87.7,88.4,89.1,89.7,90.4,91.0,91.7,92.3,93.0,93.6,94.2,94.9,95.5,96.1,96.7,97.4,98.0,98.6,99.2,99.8,100.4,101.0,101.6,102.2,102.8,103.3], p50: [49.1,53.7,57.1,59.8,62.1,64.0,65.7,67.3,68.7,70.1,71.5,72.8,74.0,75.2,76.4,77.5,78.6,79.7,80.7,81.7,82.7,83.7,84.6,85.5,86.4,87.3,88.2,89.1,89.9,90.7,91.6,92.4,93.2,94.0,94.8,95.6,96.4,97.2,97.9,98.7,99.4,100.2,100.9,101.7,102.4,103.1,103.8,104.5,105.2,105.9,106.6,107.3,108.0,108.7,109.4,110.0,110.7,111.4,112.0,112.7,113.3], p97: [52.9,57.4,61.1,64.0,66.4,68.5,70.3,71.9,73.5,75.0,76.4,77.8,79.2,80.5,81.7,83.0,84.2,85.4,86.5,87.6,88.7,89.8,90.9,92.0,93.0,94.1,95.1,96.1,97.1,98.1,99.1,100.1,101.0,102.0,103.0,103.9,104.8,105.8,106.7,107.6,108.5,109.4,110.3,111.2,112.1,113.0,113.9,114.8,115.7,116.5,117.4,118.3,119.1,120.0,120.8,121.7,122.5,123.4,124.2,125.0,125.9] }; function calcPercentil(value, ageMonths, table) { const idx = Math.min(Math.max(Math.round(ageMonths), 0), 60); const p3 = table.p3[idx], p50 = table.p50[idx], p97 = table.p97[idx]; if (value <= p3) return Math.max(1, Math.round(3 * (value / p3))); if (value <= p50) return Math.round(3 + 47 * ((value - p3) / (p50 - p3))); if (value <= p97) return Math.round(50 + 47 * ((value - p50) / (p97 - p50))); return Math.min(99, 97 + Math.round(3 * ((value - p97) / p97))); } function getPercentilColor(p) { if (p < 3 || p > 97) return '#ef4444'; if (p < 15 || p > 85) return '#f59e0b'; return '#10b981'; } function getPercentilLabel(p) { if (p < 3) return 'Bajo (P97)'; } let growthChartW = null, growthChartH = null; async function renderCrecimiento() { const sec = document.getElementById('sec-crecimiento'); sec.innerHTML = `
📈 Curvas de Crecimiento OMS
Tablas OMS 2006 — 0 a 60 meses
📈

Selecciona un paciente para ver su curva de crecimiento

Los datos se registran automaticamente en cada consulta

`; try { const patients = await api('GET', '/patients?limit=100'); const sel = document.getElementById('growthPatientSel'); const list = patients.patients || patients; list.forEach(p => { const opt = document.createElement('option'); opt.value = p.id; opt.textContent = p.first_name + ' ' + p.last_name + ' (' + calcAge(p.birth_date) + ')'; opt.dataset.sex = p.sex; opt.dataset.birth = p.birth_date; sel.appendChild(opt); }); } catch(e) { console.error(e); } } // CDC charts function getCdcChartUrl(sex, ageMonths) { const base = 'http://144.126.143.34:8095/growth-charts/'; const f = sex === 'F' || sex === 'f'; if (ageMonths <= 36) { return f ? [{url:base+'co06l018.pdf',label:'Ninas 0-36m: Talla/Peso'},{url:base+'co06l019.pdf',label:'Ninas 0-36m: Perimetro Cefalico'}] : [{url:base+'co06l020.pdf',label:'Ninos 0-36m: Talla/Peso'},{url:base+'co06l021.pdf',label:'Ninos 0-36m: Perimetro Cefalico'}]; } else { return f ? [{url:base+'co06l022.pdf',label:'Ninas 2-20a: Talla/Peso'},{url:base+'co06l023.pdf',label:'Ninas 2-20a: IMC'}] : [{url:base+'co06l024.pdf',label:'Ninos 2-20a: Talla/Peso'},{url:base+'co06l025.pdf',label:'Ninos 2-20a: IMC'}]; } } function renderCdcButtons(sex, ageMonths) { const c = document.getElementById('cdcChartBtns'); if (!c) return; const charts = getCdcChartUrl(sex, ageMonths); c.style.display = 'flex'; c.innerHTML = 'Graficas CDC:' + charts.map(ch => '' + ch.label + '').join(''); } async function loadGrowthData() { const sel = document.getElementById('growthPatientSel'); const patientId = sel.value; if (!patientId) return; const sex = sel.options[sel.selectedIndex].dataset.sex || 'M'; const birthDate = sel.options[sel.selectedIndex].dataset.birth; const ageMs = birthDate ? (Date.now() - new Date(birthDate)) / (30.44*24*60*60*1000) : 12; renderCdcButtons(sex, ageMs); document.getElementById('growthEmpty').style.display = 'none'; document.getElementById('growthContent').style.display = 'block'; document.getElementById('growthSummary').innerHTML = '
'; try { const data = await api('GET', '/patients/' + patientId + '/growth'); const records = Array.isArray(data) ? data : (data.records || []); const wtable = sex === 'M' ? WHO_WEIGHT_M : WHO_WEIGHT_F; const htable = sex === 'M' ? WHO_HEIGHT_M : WHO_HEIGHT_F; if (records.length === 0) { document.getElementById('growthSummary').innerHTML = '
Sin registros de crecimiento aun. Los datos se registran durante las consultas (Paso 1: Signos Vitales).
'; document.getElementById('growthTable').innerHTML = '

Sin datos registrados

'; renderGrowthCharts([], sex, birthDate); return; } const last = records[records.length - 1]; const ageM = last.age_months || Math.round((new Date() - new Date(birthDate)) / (1000*60*60*24*30.44)); const pW = last.weight_kg ? calcPercentil(parseFloat(last.weight_kg), ageM, wtable) : null; const pH = last.height_cm ? calcPercentil(parseFloat(last.height_cm), ageM, htable) : null; document.getElementById('growthSummary').innerHTML = '
' + (last.weight_kg ? '
' + last.weight_kg + ' kg
Peso actual
P' + pW + ' — ' + getPercentilLabel(pW) + '
' : '') + (last.height_cm ? '
' + last.height_cm + ' cm
Talla actual
P' + pH + ' — ' + getPercentilLabel(pH) + '
' : '') + (last.head_circ_cm ? '
' + last.head_circ_cm + ' cm
Perimetro Cefalico
' : '') + (last.bmi ? '
' + parseFloat(last.bmi).toFixed(1) + '
IMC
' : '') + '
'; document.getElementById('growthTable').innerHTML = '
' + records.map(r => { const am = r.age_months || 0; const pw = r.weight_kg ? calcPercentil(parseFloat(r.weight_kg), am, wtable) : null; const ph = r.height_cm ? calcPercentil(parseFloat(r.height_cm), am, htable) : null; return ''; }).join('') + '
FechaEdadPeso (kg)Percentil PesoTalla (cm)Percentil TallaPC (cm)IMC
' + (r.measurement_date ? new Date(r.measurement_date).toLocaleDateString('es-HN') : '-') + '' + am + ' meses' + (r.weight_kg || '-') + '' + (pw !== null ? 'P' + pw + ' — ' + getPercentilLabel(pw) + '' : '-') + '' + (r.height_cm || '-') + '' + (ph !== null ? 'P' + ph + ' — ' + getPercentilLabel(ph) + '' : '-') + '' + (r.head_circ_cm || '-') + '' + (r.bmi ? parseFloat(r.bmi).toFixed(1) : '-') + '
'; renderGrowthCharts(records, sex, birthDate); } catch(e) { document.getElementById('growthSummary').innerHTML = '
Error: ' + e.message + '
'; } } function renderGrowthCharts(records, sex, birthDate) { const wtable = sex === 'M' ? WHO_WEIGHT_M : WHO_WEIGHT_F; const htable = sex === 'M' ? WHO_HEIGHT_M : WHO_HEIGHT_F; if (growthChartW) { growthChartW.destroy(); growthChartW = null; } if (growthChartH) { growthChartH.destroy(); growthChartH = null; } const patW = records.filter(r => r.weight_kg).map(r => ({ x: r.age_months || 0, y: parseFloat(r.weight_kg) })); const patH = records.filter(r => r.height_cm).map(r => ({ x: r.age_months || 0, y: parseFloat(r.height_cm) })); const xLabels = Array.from({length: 61}, (_, i) => i); const commonOpts = { responsive: true, plugins: { legend: { position: 'bottom', labels: { font: { size: 10 }, boxWidth: 12 } } }, scales: { x: { title: { display: true, text: 'Edad (meses)', font: { size: 11 } }, ticks: { font: { size: 10 } } }, y: { ticks: { font: { size: 10 } } } } }; const ctxW = document.getElementById('chartPeso').getContext('2d'); growthChartW = new Chart(ctxW, { type: 'line', data: { labels: xLabels, datasets: [ { label: 'P97', data: wtable.p97, borderColor: '#fca5a5', borderWidth: 1, borderDash: [5,3], pointRadius: 0, fill: false }, { label: 'P50', data: wtable.p50, borderColor: '#6ee7b7', borderWidth: 2, pointRadius: 0, fill: false }, { label: 'P3', data: wtable.p3, borderColor: '#fca5a5', borderWidth: 1, borderDash: [5,3], pointRadius: 0, fill: false }, { label: 'Paciente', data: patW, borderColor: '#0d9488', backgroundColor: '#0d9488', borderWidth: 2.5, pointRadius: 5, pointHoverRadius: 7, fill: false, parsing: { xAxisKey: 'x', yAxisKey: 'y' } } ] }, options: { ...commonOpts, scales: { ...commonOpts.scales, y: { ...commonOpts.scales.y, title: { display: true, text: 'kg', font: { size: 11 } } } } } }); const ctxH = document.getElementById('chartTalla').getContext('2d'); growthChartH = new Chart(ctxH, { type: 'line', data: { labels: xLabels, datasets: [ { label: 'P97', data: htable.p97, borderColor: '#fca5a5', borderWidth: 1, borderDash: [5,3], pointRadius: 0, fill: false }, { label: 'P50', data: htable.p50, borderColor: '#93c5fd', borderWidth: 2, pointRadius: 0, fill: false }, { label: 'P3', data: htable.p3, borderColor: '#fca5a5', borderWidth: 1, borderDash: [5,3], pointRadius: 0, fill: false }, { label: 'Paciente', data: patH, borderColor: '#2563eb', backgroundColor: '#2563eb', borderWidth: 2.5, pointRadius: 5, pointHoverRadius: 7, fill: false, parsing: { xAxisKey: 'x', yAxisKey: 'y' } } ] }, options: { ...commonOpts, scales: { ...commonOpts.scales, y: { ...commonOpts.scales.y, title: { display: true, text: 'cm', font: { size: 11 } } } } } }); } // ============================================================ // FIN CURVAS DE CRECIMIENTO // ============================================================ // ============================================================ // REPORTES // ============================================================ async function renderReportes() { const sec = document.getElementById('sec-reportes'); sec.innerHTML = '
'; try { const summary = await api('GET', '/reports/summary'); const from = new Date(Date.now() - 30*24*60*60*1000).toISOString().split('T')[0]; const to = new Date().toISOString().split('T')[0]; const [diagnoses, vaccinesPending] = await Promise.all([ api('GET', `/reports/diagnoses?from=${from}&to=${to}`), api('GET', '/reports/vaccines-pending') ]); sec.innerHTML = `
${summary.total_patients}
Pacientes Totales
👶
${summary.today_controls}
Controles Hoy
📋
${summary.today_consults}
Consultas Hoy
🩺
${summary.pending_exams}
Exámenes Pendientes
🔬
🏥 Diagnósticos Frecuentes (últimos 30 días)
${!diagnoses || diagnoses.length === 0 ? '

Sin datos

' : diagnoses.slice(0,10).map(d => `
${d.diagnosis}
${d.total}
`).join('')}
💉 Vacunas Pendientes por Tipo
${!vaccinesPending || vaccinesPending.length === 0 ? '

Sin datos

' : vaccinesPending.slice(0,10).map(v => `
${v.vaccine}
${v.patients_pending} pacientes
`).join('')}
`; } catch (e) { sec.innerHTML = `
Error: ${e.message}
`; } } // ============================================================ // CONFIGURACIÓN // ============================================================ async function renderConfiguracion() { const sec = document.getElementById('sec-configuracion'); sec.innerHTML = '
'; try { const [settings, users, controlTypes] = await Promise.all([ api('GET', '/settings'), api('GET', '/auth/users'), api('GET', '/settings/control-types') ]); if (!settings) { sec.innerHTML = '
Sesión expirada. Por favor recarga la página.
'; return; } sec.innerHTML = `
🏥 Datos de la Clínica
`; cargarAuditoria(); } catch (e) { sec.innerHTML = `
Error: ${e.message}
`; } } function switchCfgTab(btn, tabId) { document.querySelectorAll('#sec-configuracion .tab-btn').forEach(b => b.classList.remove('active')); btn.classList.add('active'); document.querySelectorAll('#sec-configuracion [id^="cfg-"]').forEach(t => t.style.display = 'none'); document.getElementById(tabId).style.display = 'block'; } async function guardarConfiguracion() { try { await api('PUT', '/settings', { clinic_name: document.getElementById('cfgClinicName').value, clinic_hospital: document.getElementById('cfgHospital').value, doctor_name: document.getElementById('cfgDoctorName').value, doctor_specialty: document.getElementById('cfgSpecialty').value, clinic_assistant: document.getElementById('cfgAssistant').value, clinic_phone: document.getElementById('cfgPhone').value, doctor_cell: document.getElementById('cfgDoctorCell').value, clinic_email: document.getElementById('cfgEmail').value, clinic_address: document.getElementById('cfgAddress').value, clinic_schedule: document.getElementById('cfgSchedule').value, currency: document.getElementById('cfgCurrency').value, whatsapp_number: document.getElementById('cfgWhatsappNumber').value }); toast('✅ Configuración guardada', 'success'); document.getElementById('sidebarClinic').textContent = document.getElementById('cfgClinicName').value; } catch (e) { toast('Error: ' + e.message, 'error'); } } async function guardarPrecios() { try { await api('PUT', '/settings', { price_consulta: document.getElementById('priceConsulta').value, price_control: document.getElementById('priceControl').value, price_vacuna: document.getElementById('priceVacuna').value, price_examen: document.getElementById('priceExamen').value, price_urgencia: document.getElementById('priceUrgencia').value, price_nebulizacion: document.getElementById('priceNebulizacion').value, price_procedimiento: document.getElementById('priceProcedimiento').value, price_certificado: document.getElementById('priceCertificado').value }); toast('✅ Precios guardados', 'success'); } catch (e) { toast('Error: ' + e.message, 'error'); } } async function cargarAuditoria() { const wrap = document.getElementById('auditTableWrap'); if (!wrap) return; wrap.innerHTML = '
'; try { const data = await api('GET', '/support/audit?limit=50'); if (!data || !data.logs) { wrap.innerHTML = '

Sin registros

'; return; } wrap.innerHTML = `
${data.logs.map(l => ` `).join('')}
FechaUsuarioAcciónMóduloDetalle
${new Date(l.created_at).toLocaleString()} ${l.username || '-'} ${l.action} ${l.entity_type || '-'} ${l.description || '-'}
`; } catch (e) { wrap.innerHTML = `
Error: ${e.message}
`; } } // ============================================================ // HELPERS ADICIONALES // ============================================================ async function searchPatientForExam(val) { if (val.length < 2) return; const data = await api('GET', `/patients?search=${encodeURIComponent(val)}&limit=5`); const el = document.getElementById('exPatientResults'); if (!el || !data) return; el.innerHTML = data.patients.map(p => `
${p.first_name} ${p.last_name} ${p.expediente}
`).join(''); } async function searchPatientForRx(val) { if (val.length < 2) return; const data = await api('GET', `/patients?search=${encodeURIComponent(val)}&limit=5`); const el = document.getElementById('rxPatientResults'); if (!el || !data) return; el.innerHTML = data.patients.map(p => `
${p.first_name} ${p.last_name} ${p.expediente}
`).join(''); } async function searchPatientExams(val) { if (val.length < 2) return; const data = await api('GET', `/patients?search=${encodeURIComponent(val)}&limit=5`); const el = document.getElementById('examPatientResults'); if (!el || !data) return; el.innerHTML = data.patients.map(p => `
${p.sex==='M'?'👦':'👧'}
${p.first_name} ${p.last_name}
${p.expediente}
`).join(''); } async function loadPatientExamsList(pid, name) { const data = await api('GET', `/exams/patient/${pid}`); const el = document.getElementById('examPatientResults'); if (!el) return; el.innerHTML = `

Exámenes de ${name}

` + (data && data.length > 0 ? `
${data.map(e => ` `).join('')}
ExamenTipoFechaEstadoResultado
${e.exam_name} ${e.exam_type} ${formatDate(e.request_date)} ${e.status} ${e.result_value ? e.result_value.substring(0,50) : '-'}
` : '

Sin exámenes registrados

'); } function abrirNuevoUsuario() { openModal(` `); } async function guardarUsuario() { try { await api('POST', '/auth/users', { username: document.getElementById('nuUser').value, password: document.getElementById('nuPass').value, full_name: document.getElementById('nuName').value, role: document.getElementById('nuRole').value, email: document.getElementById('nuEmail').value, phone: document.getElementById('nuPhone').value }); toast('✅ Usuario creado', 'success'); closeModal(); renderConfiguracion(); } catch (e) { toast('Error: ' + e.message, 'error'); } } function verControlDetalle(id) { api('GET', `/controls/${id}`).then(c => { if (!c) return; openModal(` `); }); } function editarPaciente(id) { toast('Función de edición disponible próximamente', 'info'); } // ============================================================ // TOGGLE NÚMERO DE SEGURO // ============================================================ function toggleInsuranceNum(val) { const g = document.getElementById('pInsuranceNumGroup'); if (g) g.style.display = (val === 'ihss' || val === 'privado') ? 'block' : 'none'; } // ============================================================ // ELIMINAR PACIENTE (solo admin) // ============================================================ async function eliminarPaciente(id) { if (!confirm('¿Eliminar este paciente? Esta acción no se puede deshacer.')) return; try { await api('DELETE', '/patients/' + id); toast('Paciente eliminado', 'success'); closeModal(); renderPacientes(); } catch(e) { toast('Error: ' + e.message, 'error'); } } // ============================================================ // IMPRIMIR RECETA — IDÉNTICA A CLÍNICAS VIERA (MEDIA CARTA) // ============================================================ function imprimirReceta(id) { api('GET', `/prescriptions/${id}`).then(r => { if (!r) return; const win = window.open('', '_blank'); const fechaFmt = new Date(r.prescription_date || new Date()).toLocaleDateString('es-HN', {day:'2-digit',month:'2-digit',year:'numeric'}); const edad = r.birth_date ? calcAge(r.birth_date) : ''; const sexoFmt = r.sex === 'M' ? 'Masculino' : r.sex === 'F' ? 'Femenino' : ''; const medsHtml = (r.medications || []).map(m => `
${m.nombre} ${m.presentacion}
${m.dosis}
${m.instrucciones ? `
${m.instrucciones}
` : ''}
` ).join(''); win.document.write(` Receta Médica
🩺
Dr. Martin A. Medina M.
PEDIATRA
Especialista en enfermedades respiratorias del niño y el adolecente
Nombre: ${r.first_name || ''} ${r.last_name || ''} Edad: ${edad}
Sexo: ${sexoFmt} Fecha: ${fechaFmt}
Rx
${medsHtml}
`); win.document.close(); setTimeout(() => win.print(), 600); api('PATCH', `/prescriptions/${id}/printed`, {}); }); } // ============================================================ // INICIO // ============================================================ window.addEventListener('DOMContentLoaded', () => { if (TOKEN && USER) { initApp(); } // Cerrar sidebar al hacer click fuera en mobile document.addEventListener('click', (e) => { if (window.innerWidth <= 768 && !e.target.closest('.sidebar') && !e.target.closest('.hamburger')) { closeSidebar(); } }); });