import { useState } from 'react'; import { toast } from 'sonner'; import { User } from 'lucide-react'; import { useHospitalStore } from '@/hooks/useHospitalStore'; import { getNombreProfesional } from '@/lib/utils'; import { Accordion, AccordionContent, AccordionItem, AccordionTrigger, } from "@/components/ui/accordion"; import { FlaskConical, Microscope, Plus, Calendar, Droplet, AlertCircle, CheckCircle2, ArrowLeft, Pencil, Trash2, FileText, List, FileDown, Thermometer, Heart, Wind, Droplets, Save, X, MoreHorizontal, Eye, Edit, ClipboardList, TrendingUp, Activity, Pill, Users, ListTodo, CheckSquare, Square, Copy, Clock, Search, ChevronDown } from 'lucide-react'; import { Card, CardContent } from '@/components/ui/card'; import { Badge } from '@/components/ui/badge'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; import { Textarea } from '@/components/ui/textarea'; import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/dialog'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'; import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table'; import { ScrollArea, ScrollBar } from "@/components/ui/scroll-area" import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from '@/components/ui/dropdown-menu'; import { LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer } from 'recharts'; import type { Laboratorio, Glucemia, AcidoBase, Cultivo, Paciente, ResultadoLaboratorio, Internacion, Evolucion, Cama, EstudioComplementario, Interconsulta, ATB, Indicacion, MovimientoIndicacion, Pendiente } from '@/types'; import { formatDateDDMMYYYY, getLocalToday } from '@/lib/utils'; import { SeccionIndicaciones } from './SeccionIndicaciones'; interface HistoriaClinicaProps { internacion: Internacion; paciente: Paciente; cama?: Cama; evoluciones: Evolucion[]; laboratorios: Laboratorio[]; glucemias: Glucemia[]; acidosBase: AcidoBase[]; cultivos: Cultivo[]; estudiosComplementarios: EstudioComplementario[]; interconsultas: Interconsulta[]; atb: ATB[]; indicadores: Indicacion[]; movimientos?: MovimientoIndicacion[]; canEdit: boolean; onAgregarEvolucion: (evolucion: Omit) => void; onActualizarEvolucion: (id: string, datos: Partial) => void; onEliminarEvolucion: (id: string) => void; onAgregarLaboratorio: (laboratorios: Omit) => void; onActualizarLaboratorio: (id: string, datos: Partial) => void; onEliminarLaboratorio: (id: string) => void; onAgregarGlucemia: (glucemia: Omit) => void; onActualizarGlucemia: (id: string, datos: Partial) => void; onEliminarGlucemia: (id: string) => void; onAgregarLaboratorioConAcidoBase?: (l: Omit, a?: Omit) => void; onAgregarAcidoBase: (acidoBase: Omit) => void; onActualizarAcidoBase: (id: string, datos: Partial) => void; onEliminarAcidoBase: (id: string) => void; onAgregarCultivo: (cultivo: Omit) => void; onActualizarCultivo: (id: string, datos: Partial) => void; onEliminarCultivo: (id: string) => void; onAgregarEstudioComplementario: (estudio: Omit) => void; onActualizarEstudioComplementario: (id: string, datos: Partial) => void; onEliminarEstudioComplementario: (id: string) => void; onAgregarInterconsulta: (interconsulta: Omit) => void; onActualizarInterconsulta: (id: string, datos: Partial) => void; onEliminarInterconsulta: (id: string) => void; onAgregarATB: (atb: Omit) => void; onActualizarATB: (id: string, datos: Partial) => void; onEliminarATB: (id: string) => void; onAgregarIndicacion: (indicacion: Omit) => void; onActualizarIndicacion: (id: string, datos: Partial) => void; onEliminarIndicacion: (id: string) => void; onAgregarMovimiento?: (movimiento: MovimientoIndicacion) => void; onVolver: () => void; onEditarIngreso?: () => void; } const RANGOS_LABORATORIO: Record = { 'Hematocrito': { min: 36, max: 42 }, 'Hemoglobina': { min: 12, max: 14 }, 'Leucocitos': { min: 4000, max: 11000 }, 'Plaquetas': { min: 150000, max: 400000 }, 'Glucosa': { min: 70, max: 100 }, 'Urea': { min: 17, max: 48.5 }, 'Creatinina': { min: 0.5, max: 1.2 }, 'Sodio': { min: 135, max: 145 }, 'Potasio': { min: 3.5, max: 5.1 }, 'Cloro': { min: 101, max: 109 }, 'Bilirrubina Total': { min: 0.1, max: 1.2 }, 'Bilirrubina Directa': { min: 0, max: 0.3 }, 'GOT': { min: 0, max: 35 }, 'GPT': { min: 0, max: 35 }, 'Tiempo de Protrombina': { min: 12, max: 14 }, 'KPTT': { min: 30, max: 45 }, 'INR': { min: 0.8, max: 1.5 }, 'Colesterol Total': { min: 0, max: 200 }, 'Colesterol LDL': { min: 0, max: 130 }, 'Colesterol No HDL': { min: 0, max: 160 }, 'Colesterol HDL': { min: 40, max: 100 }, 'Triglicéridos': { min: 0, max: 150 }, 'Albúmina': { min: 3.5, max: 5.0 }, 'Calcio Total': { min: 8.5, max: 10.5 }, 'Fosfatasa Alcalina': { min: 44, max: 147 }, 'LDH': { min: 140, max: 280 }, 'Hierro': { min: 50, max: 170 }, 'Transferrina': { min: 200, max: 360 }, 'Porcentaje de Saturación de Transferrina': { min: 20, max: 50 }, 'Ferritina': { min: 10, max: 300 }, 'Ácido Fólico': { min: 3, max: 17 }, 'Vitamina B12': { min: 200, max: 900 }, 'NT-proBNP': { min: 0, max: 125 }, 'Procalcitonina': { min: 0, max: 0.5 }, 'Fósforo': { min: 2.5, max: 4.5 }, 'Magnesio': { min: 1.6, max: 2.6 }, 'Calcio Iónico': { min: 1.12, max: 1.32 }, }; function calcularEstadoLaboratorio(parametro: string, valor: string): 'Normal' | 'Alto' | 'Bajo' { const rango = RANGOS_LABORATORIO[parametro]; if (!rango) return 'Normal'; const num = parseFloat(valor); if (isNaN(num)) return 'Normal'; if (parametro === 'Tiempo de Protrombina' && num > 50) { if (num < 70) return 'Bajo'; if (num > 100) return 'Alto'; return 'Normal'; } if (parametro === 'Calcio Iónico') { if (num < 2) { if (num < 1.12) return 'Bajo'; if (num > 1.32) return 'Alto'; return 'Normal'; } else { if (num < 4.5) return 'Bajo'; if (num > 5.6) return 'Alto'; return 'Normal'; } } if (num < rango.min) return 'Bajo'; if (num > rango.max) return 'Alto'; return 'Normal'; } function wrapText(text: string, font: { widthOfTextAtSize: (text: string, size: number) => number }, fontSize: number, maxWidth: number): string[] { const words = text.split(' '); const lines: string[] = []; let currentLine = ''; for (const word of words) { const testLine = currentLine ? `${currentLine} ${word}` : word; const testWidth = font.widthOfTextAtSize(testLine, fontSize); if (testWidth > maxWidth && currentLine) { lines.push(currentLine); currentLine = word; } else { currentLine = testLine; } } if (currentLine) lines.push(currentLine); return lines; } export function HistoriaClinica({ internacion, paciente, cama, evoluciones, laboratorios, glucemias, acidosBase, cultivos, estudiosComplementarios, interconsultas, onAgregarEvolucion, onActualizarEvolucion, onEliminarEvolucion, onAgregarLaboratorio, onActualizarLaboratorio, onEliminarLaboratorio, onAgregarGlucemia, onActualizarGlucemia, onEliminarGlucemia, onAgregarAcidoBase, onActualizarAcidoBase, onEliminarAcidoBase, onAgregarCultivo, onActualizarCultivo, onEliminarCultivo, onAgregarEstudioComplementario, onActualizarEstudioComplementario, onEliminarEstudioComplementario, onAgregarInterconsulta, onActualizarInterconsulta, onEliminarInterconsulta, atb, indicadores, onAgregarATB, onActualizarATB, onEliminarATB, onAgregarIndicacion, onActualizarIndicacion, onEliminarIndicacion, movimientos, onAgregarMovimiento, onVolver, onEditarIngreso, canEdit, }: HistoriaClinicaProps) { const [tabActivo, setTabActivo] = useState('evoluciones'); const { pendientes } = useHospitalStore(); const indicacionesActivas = (indicadores || []).filter(i => i.estado === 'Activa'); const pendientesActivos = (pendientes || []).filter(p => p.pacienteId === paciente.id && p.estado === 'pendiente'); const calcularEdad = (fechaNacimiento: string) => { const hoy = new Date(); const nac = new Date(fechaNacimiento); let edad = hoy.getFullYear() - nac.getFullYear(); const mes = hoy.getMonth() - nac.getMonth(); if (mes < 0 || (mes === 0 && hoy.getDate() < nac.getDate())) { edad--; } return edad; }; const calcularDiasInternado = (fechaIngreso: string) => { const hoy = new Date(); const ingreso = new Date(fechaIngreso); const diff = hoy.getTime() - ingreso.getTime(); return Math.floor(diff / (1000 * 60 * 60 * 24)); }; return (

{cama?.numero || 'N/A'} - Historia Clínica

Módulo de Historia Clínica de Internación

{canEdit && onEditarIngreso && ( )}

{paciente.apellido}, {paciente.nombre}

7 ? 'bg-red-100 text-red-800' : 'bg-blue-100 text-blue-800'}> {internacion.fechaIngresoClinica ? calcularDiasInternado(internacion.fechaIngresoClinica) : 0} días internado
{paciente.fechaNacimiento ? calcularEdad(paciente.fechaNacimiento) + ' años' : ''} DNI: {paciente.dni}
HC: {paciente.historiaClinica || 'No posee'} OS: {paciente.obraSocial || 'No posee'} Nacionalidad: {paciente.nacionalidad || 'N/A'}

Antecedentes

{paciente.antecedentes || 'No refiere'}

MH

{paciente.medicacionHabitual || 'No refiere'}

Fecha de Ingreso

{internacion.fechaIngresoClinica ? formatDateDDMMYYYY(internacion.fechaIngresoClinica) : 'N/A'}

Diagnóstico de Ingreso

{internacion.diagnosticoIngreso}

{!internacion.activa && ( <>

Fecha de Egreso

{internacion.fechaEgreso ? formatDateDDMMYYYY(internacion.fechaEgreso) : 'N/A'}

Diagnóstico de Egreso

{internacion.diagnosticoEgreso || 'N/A'}

Motivo de Egreso

{internacion.motivoEgreso || 'N/A'} {internacion.motivoEgreso === 'Pase servicio' && internacion.servicioAlQuePasa ? ` (${internacion.servicioAlQuePasa})` : ''}

)} Detalle Episodio Actual

Motivo de Consulta

{internacion.motivoConsulta || 'Sin motivo registrado'}

Enfermedad Actual

{internacion.enfermedadActual || 'Sin información'}

Antecedentes de Enfermedad Actual

{internacion.antecedentesEnfermedadActual || 'Sin antecedentes registrados'}

Evoluciones ({evoluciones.length}) Indicaciones ({indicacionesActivas.length}) Glucemias ({glucemias.length}) Laboratorio ({laboratorios.length}) EAB ({acidosBase.length}) Cultivos ({cultivos.length}) ATB ({atb.length}) Estudios ({estudiosComplementarios.length}) Interconsultas ({interconsultas.length}) Pendientes ({pendientesActivos.length})
); } function SeccionGlucemias({ glucemias, patientId, internacionId, add, update, del, canEdit }: { glucemias: Glucemia[]; patientId: string; internacionId: string; add: (g: Omit) => void; update: (id: string, data: Partial) => void; del: (id: string) => void; canEdit?: boolean; }) { const [dialog, setDialog] = useState(false); const [evolDialog, setEvolDialog] = useState(false); const [edit, setEdit] = useState(null); const [fecha, setFecha] = useState(getLocalToday()); const [hora, setHora] = useState(new Date().toTimeString().slice(0, 5)); const [valor, setValor] = useState(''); const [correccion, setCorreccion] = useState(''); const reset = () => { setEdit(null); setFecha(getLocalToday()); setHora(new Date().toTimeString().slice(0, 5)); setValor(''); setCorreccion(''); }; const loadEdit = (g: Glucemia) => { setEdit(g); setFecha(g.fecha); setHora(g.hora || ''); setValor(g.valor.toString()); setCorreccion(g.correccion.toString()); setDialog(true); }; const handleGuardar = () => { const valNum = parseFloat(valor); const corrNum = parseFloat(correccion); if (isNaN(valNum)) { toast.error('El valor de glucemia debe ser un número válido'); return; } if (isNaN(corrNum)) { toast.error('El valor de corrección debe ser un número válido'); return; } if (edit) { update(edit.id, { fecha, hora, valor: valNum, correccion: corrNum }); toast.success('Glucemia actualizada correctamente'); } else { add({ pacienteId: patientId, internacionId, fecha, hora, valor: valNum, correccion: corrNum }); toast.success('Glucemia registrada correctamente'); } setDialog(false); reset(); }; const listGlucemias = (glucemias || []) .filter(g => g.pacienteId === patientId) .sort((a, b) => { const dateA = new Date(a.fecha + 'T' + (a.hora || '00:00')).getTime(); const dateB = new Date(b.fecha + 'T' + (b.hora || '00:00')).getTime(); return dateB - dateA; }); const datosEvolucion = [...listGlucemias] .reverse() .map(g => ({ fechaHora: `${formatDateDDMMYYYY(g.fecha)} ${g.hora || ''}`, glucemia: g.valor, correccion: g.correccion })); return (
{canEdit && ( )}
{edit ? 'Editar' : 'Nueva'} Glucemia
setFecha(e.target.value)} />
setHora(e.target.value)} />
setValor(e.target.value)} />
setCorreccion(e.target.value)} />
Evolución de Glucemias
{datosEvolucion.length > 0 ? (
) : (
No hay registros de glucemia para graficar
)}
{listGlucemias.length === 0 ? (
No hay registros de glucemias.
) : (
Fecha y Hora Glucemia (Mg%) Corrección (UI) + {listGlucemias.map(g => ( {formatDateDDMMYYYY(g.fecha)} {g.hora || ''} {g.valor} Mg% {g.correccion > 0 ? ( {g.correccion} UI ) : ( Sin corrección )} loadEdit(g)}> Editar del(g.id)} className="text-red-600"> Eliminar ))}
)}
); } function SeccionLaboratorios({ lab, patientId, internacionId, add, update, del, addAcidoBase, canEdit }: { lab: Laboratorio[]; patientId: string; internacionId: string; add: (l: Omit) => void; update: (id: string, data: Partial) => void; del: (id: string) => void; addAcidoBase?: (a: Omit) => void; canEdit?: boolean; }) { const [dialog, setDialog] = useState(false); const [obsDialog, setObsDialog] = useState(false); const [otrosDialog, setOtrosDialog] = useState(false); const [evolDialog, setEvolDialog] = useState(false); const [edit, setEdit] = useState(null); const [obsLab, setObsLab] = useState(null); const [fecha, setFecha] = useState(getLocalToday()); const [hora, setHora] = useState(new Date().toTimeString().slice(0, 5)); const [observaciones, setObservaciones] = useState(''); const [hto, setHto] = useState(''); const [hb, setHb] = useState(''); const [gb, setGb] = useState(''); const [plaq, setPlaq] = useState(''); const [gluc, setGluc] = useState(''); const [urea, setUrea] = useState(''); const [creat, setCreat] = useState(''); const [na, setNa] = useState(''); const [k, setK] = useState(''); const [cl, setCl] = useState(''); const [bt, setBt] = useState(''); const [bd, setBd] = useState(''); const [got, setGot] = useState(''); const [gpt, setGpt] = useState(''); const [tp, setTp] = useState(''); const [kptt, setKptt] = useState(''); const [rin, setRin] = useState(''); const [parametroEvolucion, setParametroEvolucion] = useState(''); const [importDialog, setImportDialog] = useState(false); const [importFecha, setImportFecha] = useState(getLocalToday()); const [importHora, setImportHora] = useState(new Date().toTimeString().slice(0, 5)); const [importTexto, setImportTexto] = useState(''); const [importResultados, setImportResultados] = useState([]); const [importObservaciones, setImportObservaciones] = useState(''); const [importAcidoBase, setImportAcidoBase] = useState | null>(null); const [labSortAsc, setLabSortAsc] = useState(false); const parametrosLaboratorio = [ 'Hematocrito', 'Hemoglobina', 'Leucocitos', 'Plaquetas', 'Glucemia', 'Urea', 'Creatinina', 'Sodio', 'Potasio', 'Cloro', 'Bilirrubina Total', 'Bilirrubina Directa', 'GOT', 'GPT', 'Tiempo de Protrombina', 'KPTT', 'INR' ]; const datosEvolucion = lab .filter(l => l.pacienteId === patientId && l.resultados.some(r => r.parametro === parametroEvolucion)) .sort((a, b) => new Date(a.fecha + 'T' + (a.hora || '00:00')).getTime() - new Date(b.fecha + 'T' + (b.hora || '00:00')).getTime()) .map(l => { const r = l.resultados.find(r => r.parametro === parametroEvolucion); return { fecha: `${l.fecha}${l.hora ? ' ' + l.hora : ''}`, valor: r ? parseFloat(String(r.valor)) : null }; }); const copiarResumenLaboratorio = (l: Laboratorio) => { const resumen = l.resultados.map(r => `${r.parametro}: ${r.valor} ${r.unidad || ''}`.trim()).join(' | '); const texto = `Laboratorio (${l.fecha}${l.hora ? ' ' + l.hora : ''}): ${resumen}${l.observaciones ? '\nObs: ' + l.observaciones : ''}`; navigator.clipboard.writeText(texto); toast.success('Resumen de laboratorio copiado al portapapeles'); }; const getValor = (l: Laboratorio, param: string) => { const r = l.resultados.find(r => r.parametro === param); return r ? `${r.valor}` : '-'; }; const getValorRaw = (l: Laboratorio, param: string) => { const r = l.resultados.find(r => r.parametro === param); return r ? `${r.valor}` : ''; }; const getValorStyle = (l: Laboratorio, param: string) => { const r = l.resultados.find(r => r.parametro === param); if (!r) return ''; if (r.estado === 'Normal') return 'text-green-600 font-medium'; return 'text-red-600 font-medium'; }; const buildResultados = (): ResultadoLaboratorio[] => { const res: ResultadoLaboratorio[] = []; if (hto) res.push({ parametro: 'Hematocrito', valor: hto, unidad: '%', estado: calcularEstadoLaboratorio('Hematocrito', hto) }); if (hb) res.push({ parametro: 'Hemoglobina', valor: hb, unidad: 'g/dL', estado: calcularEstadoLaboratorio('Hemoglobina', hb) }); if (gb) res.push({ parametro: 'Leucocitos', valor: gb, unidad: '/mm3', estado: calcularEstadoLaboratorio('Leucocitos', gb) }); if (plaq) res.push({ parametro: 'Plaquetas', valor: plaq, unidad: '/mm3', estado: calcularEstadoLaboratorio('Plaquetas', plaq) }); if (gluc) res.push({ parametro: 'Glucemia', valor: gluc, unidad: 'mg/dL', estado: calcularEstadoLaboratorio('Glucemia', gluc) }); if (urea) res.push({ parametro: 'Urea', valor: urea, unidad: 'mg/dL', estado: calcularEstadoLaboratorio('Urea', urea) }); if (creat) res.push({ parametro: 'Creatinina', valor: creat, unidad: 'mg/dL', estado: calcularEstadoLaboratorio('Creatinina', creat) }); if (na) res.push({ parametro: 'Sodio', valor: na, unidad: 'mEq/L', estado: calcularEstadoLaboratorio('Sodio', na) }); if (k) res.push({ parametro: 'Potasio', valor: k, unidad: 'mEq/L', estado: calcularEstadoLaboratorio('Potasio', k) }); if (cl) res.push({ parametro: 'Cloro', valor: cl, unidad: 'mEq/L', estado: calcularEstadoLaboratorio('Cloro', cl) }); if (bt) res.push({ parametro: 'Bilirrubina Total', valor: bt, unidad: 'mg/dL', estado: calcularEstadoLaboratorio('Bilirrubina Total', bt) }); if (bd) res.push({ parametro: 'Bilirrubina Directa', valor: bd, unidad: 'mg/dL', estado: calcularEstadoLaboratorio('Bilirrubina Directa', bd) }); if (got) res.push({ parametro: 'GOT', valor: got, unidad: 'U/L', estado: calcularEstadoLaboratorio('GOT', got) }); if (gpt) res.push({ parametro: 'GPT', valor: gpt, unidad: 'U/L', estado: calcularEstadoLaboratorio('GPT', gpt) }); if (tp) res.push({ parametro: 'Tiempo de Protrombina', valor: tp, unidad: 'seg', estado: calcularEstadoLaboratorio('Tiempo de Protrombina', tp) }); if (kptt) res.push({ parametro: 'KPTT', valor: kptt, unidad: 'seg', estado: calcularEstadoLaboratorio('KPTT', kptt) }); if (rin) res.push({ parametro: 'INR', valor: rin, unidad: '', estado: calcularEstadoLaboratorio('INR', rin) }); return res; }; const reset = () => { setFecha(getLocalToday()); setHora(new Date().toTimeString().slice(0, 5)); setObservaciones(''); setHto(''); setHb(''); setGb(''); setPlaq(''); setGluc(''); setUrea(''); setCreat(''); setNa(''); setK(''); setCl(''); setBt(''); setBd(''); setGot(''); setGpt(''); setTp(''); setKptt(''); setRin(''); setEdit(null); }; const buildObservacionesFromResultados = (resultadosArray: ResultadoLaboratorio[]): string => { const observacionesExtra: string[] = []; // 1. Lípidos const ordenLipidos = ['Colesterol Total', 'Colesterol LDL', 'Colesterol No HDL', 'Colesterol HDL', 'Triglicéridos']; const lipidosEncontrados = resultadosArray.filter(r => ordenLipidos.includes(r.parametro)); if (lipidosEncontrados.length > 0) { observacionesExtra.push('PERFIL LIPIDICO:'); for (const nombre of ordenLipidos) { const item = lipidosEncontrados.find(l => l.parametro === nombre); if (item) { observacionesExtra.push(`- ${item.parametro}: ${item.valor} ${item.unidad}`); } } } // 2. Férricos const ordenFerricos = ['Hierro', 'Transferrina', 'Porcentaje de Saturación de Transferrina', 'Ferritina', 'Ácido Fólico', 'Vitamina B12']; const ferricosEncontrados = resultadosArray.filter(r => ordenFerricos.includes(r.parametro)); if (ferricosEncontrados.length > 0) { if (observacionesExtra.length > 0) { observacionesExtra.push(''); } observacionesExtra.push('PERFIL FERRICO:'); for (const nombre of ordenFerricos) { const item = ferricosEncontrados.find(f => f.parametro === nombre); if (item) { observacionesExtra.push(`- ${item.parametro}: ${item.valor} ${item.unidad}`); } } } // 3. Independientes const ordenInd = [ 'Albúmina', 'Calcio Total', 'Fosfatasa Alcalina', 'LDH', 'NT-proBNP', 'Procalcitonina', 'Fósforo', 'Magnesio', 'Calcio Iónico' ]; const indEncontrados = resultadosArray.filter(r => ordenInd.includes(r.parametro)); if (indEncontrados.length > 0) { if (observacionesExtra.length > 0) { observacionesExtra.push(''); } for (const nombre of ordenInd) { const item = indEncontrados.find(i => i.parametro === nombre); if (item) { observacionesExtra.push(`${item.parametro}: ${item.valor} ${item.unidad}`); } } } // 4. VCM & HCM const ordenHemogramaAdicionales = ['VCM', 'HCM']; const hemogramaAdicionalesEncontrados = resultadosArray.filter(r => ordenHemogramaAdicionales.includes(r.parametro)); if (hemogramaAdicionalesEncontrados.length > 0) { if (observacionesExtra.length > 0) { observacionesExtra.push(''); } for (const nombre of ordenHemogramaAdicionales) { const item = hemogramaAdicionalesEncontrados.find(h => h.parametro === nombre); if (item) { observacionesExtra.push(`${item.parametro}: ${item.valor} ${item.unidad}`); } } } return observacionesExtra.join('\n'); }; const parseLaboratorioTexto = (texto: string): { resultados: ResultadoLaboratorio[]; observaciones: string } => { const resultados: ResultadoLaboratorio[] = []; const mapeoParametros: { claves: string[]; nombre: string; unidad: string; esPrincipal: boolean; esAdicional?: boolean }[] = [ { claves: ['hematocrito', 'hto'], nombre: 'Hematocrito', unidad: '%', esPrincipal: true }, { claves: ['hemoglobina corpuscular media', 'hcm'], nombre: 'HCM', unidad: 'pg', esPrincipal: false, esAdicional: true }, { claves: ['hemoglobina', 'hb'], nombre: 'Hemoglobina', unidad: 'g/dL', esPrincipal: true }, { claves: ['volumen corpuscular medio', 'vcm'], nombre: 'VCM', unidad: 'fL', esPrincipal: false, esAdicional: true }, { claves: ['rdw'], nombre: 'RDW', unidad: '%', esPrincipal: false }, { claves: ['eritroblastos'], nombre: 'Eritroblastos', unidad: '%', esPrincipal: false }, { claves: ['neutrófilos', 'neutrofilos'], nombre: 'Neutrófilos', unidad: '%', esPrincipal: false }, { claves: ['linfocitos'], nombre: 'Linfocitos', unidad: '%', esPrincipal: false }, { claves: ['monocitos'], nombre: 'Monocitos', unidad: '%', esPrincipal: false }, { claves: ['eosinófilos', 'eosinofilos'], nombre: 'Eosinófilos', unidad: '%', esPrincipal: false }, { claves: ['basófilos', 'basofilos'], nombre: 'Basófilos', unidad: '%', esPrincipal: false }, { claves: ['recuento de leucocitos', 'glóbulos blancos', 'globulos blancos', 'leucocitos', 'gb'], nombre: 'Leucocitos', unidad: '10³/µl', esPrincipal: true }, { claves: ['recuento de plaquetas', 'plaquetas', 'plaq'], nombre: 'Plaquetas', unidad: '10³/µl', esPrincipal: true }, { claves: ['volumen plaquetario medio', 'vpm'], nombre: 'VPM', unidad: 'fL', esPrincipal: false }, { claves: ['glucosa', 'glucemia'], nombre: 'Glucemia', unidad: 'mg/dL', esPrincipal: true }, { claves: ['urea'], nombre: 'Urea', unidad: 'mg/dL', esPrincipal: true }, { claves: ['creatinina', 'creat'], nombre: 'Creatinina', unidad: 'mg/dL', esPrincipal: true }, { claves: ['mdrd'], nombre: 'Filtrado Glomerular (MDRD)', unidad: 'ml/min', esPrincipal: false }, { claves: ['sodio', 'na'], nombre: 'Sodio', unidad: 'mEq/L', esPrincipal: true }, { claves: ['potasio', 'k+', 'k'], nombre: 'Potasio', unidad: 'mEq/L', esPrincipal: true }, { claves: ['cloro', 'cl-', 'cl'], nombre: 'Cloro', unidad: 'mEq/L', esPrincipal: true }, { claves: ['bilirrubina total', 'bt'], nombre: 'Bilirrubina Total', unidad: 'mg/dL', esPrincipal: true }, { claves: ['bilirrubina directa', 'bd'], nombre: 'Bilirrubina Directa', unidad: 'mg/dL', esPrincipal: true }, { claves: ['got', 'ast'], nombre: 'GOT', unidad: 'U/L', esPrincipal: true }, { claves: ['gpt', 'alt'], nombre: 'GPT', unidad: 'U/L', esPrincipal: true }, { claves: ['proteínas totales', 'proteinas totales'], nombre: 'Proteínas Totales', unidad: 'g/dL', esPrincipal: false }, { claves: ['tiempo de protrombina', 't.p.', 't.p', 'tp', 'protrombina'], nombre: 'Tiempo de Protrombina', unidad: '%', esPrincipal: true }, { claves: ['rin', 'inr'], nombre: 'INR', unidad: '', esPrincipal: true }, { claves: ['aptt', 'kptt'], nombre: 'KPTT', unidad: 'seg', esPrincipal: true }, // Additional requested determinations { claves: ['colesterol ldl', 'ldl-c', 'ldl', 'colest. ldl', 'colest ldl'], nombre: 'Colesterol LDL', unidad: 'mg/dL', esPrincipal: false, esAdicional: true }, { claves: ['colesterol no-hdl', 'colesterol no hdl', 'no-hdl', 'no hdl', 'no_hdl'], nombre: 'Colesterol No HDL', unidad: 'mg/dL', esPrincipal: false, esAdicional: true }, { claves: ['colesterol hdl', 'hdl-c', 'hdl', 'colest. hdl', 'colest hdl'], nombre: 'Colesterol HDL', unidad: 'mg/dL', esPrincipal: false, esAdicional: true }, { claves: ['colesterol total', 'colesterol', 'colest. total', 'col. total', 'colest total'], nombre: 'Colesterol Total', unidad: 'mg/dL', esPrincipal: false, esAdicional: true }, { claves: ['triglicéridos', 'trigliceridos', 'tg', 'triglicérido', 'triglicerido'], nombre: 'Triglicéridos', unidad: 'mg/dL', esPrincipal: false, esAdicional: true }, { claves: ['albúmina', 'albumina'], nombre: 'Albúmina', unidad: 'g/dL', esPrincipal: false, esAdicional: true }, { claves: ['calcio total', 'calcio', 'ca total', 'ca+', 'ca++'], nombre: 'Calcio Total', unidad: 'mg/dL', esPrincipal: false, esAdicional: true }, { claves: ['fosfatasa alcalina', 'fal'], nombre: 'Fosfatasa Alcalina', unidad: 'U/L', esPrincipal: false, esAdicional: true }, { claves: ['ldh', 'lactato deshidrogenasa', 'lactatodeshidrogenasa'], nombre: 'LDH', unidad: 'U/L', esPrincipal: false, esAdicional: true }, { claves: ['nt-probnp', 'nt probnp', 'nt_probnp', 'probnp', 'pro-bnp', 'pro bnp'], nombre: 'NT-proBNP', unidad: 'pg/mL', esPrincipal: false, esAdicional: true }, { claves: ['procalcitonina', 'pct'], nombre: 'Procalcitonina', unidad: 'ng/mL', esPrincipal: false, esAdicional: true }, { claves: ['fósforo', 'fosforo', 'fosfemia'], nombre: 'Fósforo', unidad: 'mg/dL', esPrincipal: false, esAdicional: true }, { claves: ['magnesio', 'mg', 'magnesemia'], nombre: 'Magnesio', unidad: 'mg/dL', esPrincipal: false, esAdicional: true }, { claves: ['calcio ionico', 'calcio iónico', 'ca ionico', 'ca iónico', 'ca++ ionico', 'ca++ iónico', 'calcio_ionico'], nombre: 'Calcio Iónico', unidad: 'mmol/L', esPrincipal: false, esAdicional: true }, // Perfil Férrico requested determinations { claves: ['porcentaje de saturacion de transferrina', 'porcentaje de saturación de transferrina', 'saturacion de transferrina', 'saturación de transferrina', 'sat. transferrina', 'sat transferrina', '% sat', '% de sat', '% saturacion', '% saturación'], nombre: 'Porcentaje de Saturación de Transferrina', unidad: '%', esPrincipal: false, esAdicional: true }, { claves: ['transferrina', 'transferrin'], nombre: 'Transferrina', unidad: 'mg/dL', esPrincipal: false, esAdicional: true }, { claves: ['hierro', 'sideremia', 'fe'], nombre: 'Hierro', unidad: 'µg/dL', esPrincipal: false, esAdicional: true }, { claves: ['ferritina', 'ferritin'], nombre: 'Ferritina', unidad: 'ng/mL', esPrincipal: false, esAdicional: true }, { claves: ['acido folico', 'ácido fólico', 'folato', 'folatos', 'folico', 'fólico'], nombre: 'Ácido Fólico', unidad: 'ng/mL', esPrincipal: false, esAdicional: true }, { claves: ['vitamina b12', 'b12', 'vit. b12'], nombre: 'Vitamina B12', unidad: 'pg/mL', esPrincipal: false, esAdicional: true } ]; const matchClave = (lineaLower: string, clave: string): boolean => { if (clave.length > 4) { return lineaLower.includes(clave); } const escaped = clave.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); const regex = new RegExp(`(?:^|[^a-z0-9_])${escaped}(?:$|[^a-z0-9_])`, 'i'); return regex.test(lineaLower); }; const lineas = texto.split('\n'); for (const linea of lineas) { const lineaLower = linea.toLowerCase().trim(); if (!lineaLower) continue; for (const group of mapeoParametros) { // Skip if this parameter was already found if (resultados.some(r => r.parametro === group.nombre)) continue; let matchedClave = false; let matchedClaveString = ''; for (const clave of group.claves) { if (group.nombre === 'Colesterol Total' && clave === 'colesterol') { if (lineaLower.includes('ldl') || lineaLower.includes('hdl') || lineaLower.includes('no')) { continue; } } if (group.nombre === 'Transferrina' && clave === 'transferrina') { if (lineaLower.includes('saturac') || lineaLower.includes('sat') || lineaLower.includes('%')) { continue; } } if (matchClave(lineaLower, clave)) { matchedClave = true; matchedClaveString = clave; break; } } if (matchedClave) { let subLinea = linea; const idxClave = lineaLower.indexOf(matchedClaveString); if (idxClave !== -1) { subLinea = linea.substring(idxClave + matchedClaveString.length); } let match = subLinea.match(/(\d+(?:[.,]\d+)?)/); if (!match) { match = linea.match(/[a-zA-ZáéíóúñÁÉÍÓÚÑ]+.*?(\d+(?:[.,]\d+)?)/); } if (match && match[1]) { const valor = parseFloat(match[1].replace(',', '.')); if (!isNaN(valor) && valor > 0 && valor < 1000000) { const nombreNormalizado = group.nombre; let valorFinal = valor; if (nombreNormalizado === 'Leucocitos' || nombreNormalizado === 'Plaquetas') { if (valor < 200) valorFinal = valor * 1000; } let unidadFinal = group.unidad; if (nombreNormalizado === 'Tiempo de Protrombina') { if (lineaLower.includes('seg') || lineaLower.includes('segundo')) { unidadFinal = 'seg'; } else if (lineaLower.includes('%')) { unidadFinal = '%'; } } if (group.esPrincipal || group.esAdicional) { resultados.push({ parametro: nombreNormalizado, valor: valorFinal, unidad: unidadFinal, estado: calcularEstadoLaboratorio(nombreNormalizado, String(valorFinal)) }); } break; } } } } } const observacionesStr = buildObservacionesFromResultados(resultados); return { resultados, observaciones: observacionesStr }; }; const parseAcidoBaseTexto = (texto: string): Omit | null => { const lineas = texto.split('\n'); let ph: number | undefined; let pco2: number | undefined; let po2: number | undefined; let hco3: number | undefined; let be: number | undefined; let sato2: number | undefined; let lactato: number | undefined; let fio2: number | undefined; const fecha = importFecha; for (const linea of lineas) { const cleanLinea = linea.replace(/show_chart|list_alt|\t+/g, ' ').replace(/,/g, '.').replace(/\s+/g, ' ').trim().toLowerCase(); const getValue = (param: string): number | undefined => { const idx = cleanLinea.indexOf(param); if (idx === -1) return undefined; const after = cleanLinea.slice(idx + param.length).trim(); const parts = after.split(' '); for (const p of parts) { const v = parseFloat(p); if (!isNaN(v) && v > 0 && v < 1000) return v; } return undefined; }; if (cleanLinea.includes('estado') && cleanLinea.includes('ácido') || cleanLinea.includes('base')) { ph = getValue('ph') || (cleanLinea.match(/ph\s+(\d+\.?\d*)/)?.[1] ? parseFloat(cleanLinea.match(/ph\s+(\d+\.?\d*)/)![1]) : undefined); pco2 = getValue('pco2') || getValue('pco₂'); po2 = getValue('po2') || getValue('po₂'); hco3 = getValue('hco3'); be = getValue('exceso de base') || getValue('base excess') || getValue('exceso'); sato2 = getValue('saturación') || getValue('sato2') || getValue('sat'); lactato = getValue('lactato'); fio2 = getValue('fio2'); break; } } if (!ph) { for (const linea of lineas) { const cleanLinea = linea.replace(/show_chart|list_alt|\t+/g, ' ').replace(/,/g, '.').replace(/\s+/g, ' ').trim().toLowerCase(); const getValue = (param: string): number | undefined => { const idx = cleanLinea.indexOf(param); if (idx === -1) return undefined; const after = cleanLinea.slice(idx + param.length).trim(); const parts = after.split(' '); for (const p of parts) { const v = parseFloat(p); if (!isNaN(v) && v > 0 && v < 1000) return v; } return undefined; }; if (cleanLinea.includes('ph') && !cleanLinea.includes('pco2') && !cleanLinea.includes('pco')) { ph = getValue('ph'); } else if (cleanLinea.includes('pco2') || cleanLinea.includes('pco₂')) { pco2 = getValue('pco2'); } else if (cleanLinea.includes('po2') || cleanLinea.includes('po₂')) { po2 = getValue('po2'); } else if (cleanLinea.includes('hco3')) { hco3 = getValue('hco3'); } else if (cleanLinea.includes('exceso') || cleanLinea.includes('base excess')) { be = getValue('exceso') || getValue('base'); } else if (cleanLinea.includes('saturación') || cleanLinea.includes('sato2')) { sato2 = getValue('saturación') || getValue('sat'); } else if (cleanLinea.includes('lactato')) { lactato = getValue('lactato'); } else if (cleanLinea.includes('fio2')) { fio2 = getValue('fio2'); } } } if (ph) { return { pacienteId: patientId, fecha, hora: '', ph, pco2: pco2 || 40, po2: po2 || 85, hco3: hco3 || 24, be: be || 0, sato2: sato2 || 97, lactato, fio2, interpretacion: '' }; } return null; }; const esParametroCore = (param: string): boolean => { const coreParameters = [ // Hemograma 'hematocrito', 'hto', 'hemoglobina', 'hb', 'leucocitos', 'gb', 'plaquetas', 'plaq', 'rdw', 'eritroblastos', 'neutrófilos', 'neutrofilos', 'linfocitos', 'monocitos', 'eosinófilos', 'eosinofilos', 'basófilos', 'basofilos', 'vpm', // Glucemia 'glucemia', 'glucosa', // Urea 'urea', // Creatinina 'creatinina', 'creat', 'filtrado glomerular (mdrd)', 'mdrd', // Ionograma 'sodio', 'na', 'potasio', 'k', 'k+', 'cloro', 'cl', 'cl-', // Hepatograma 'bilirrubina total', 'bt', 'bilirrubina directa', 'bd', 'got', 'ast', 'gpt', 'alt', 'proteínas totales', 'proteinas totales', // Coagulograma 'tiempo de protrombina', 'tp', 't.p.', 't.p', 'rin', 'inr', 'aptt', 'kptt' ]; return coreParameters.includes(param.toLowerCase().trim()); }; const handleEliminarDeterminacion = (index: number) => { setImportResultados(prev => { const filtered = prev.filter((_, i) => i !== index); setImportObservaciones(buildObservacionesFromResultados(filtered)); return filtered; }); }; const handleProcesarTexto = () => { const { resultados, observaciones } = parseLaboratorioTexto(importTexto); const acidoBase = parseAcidoBaseTexto(importTexto); setImportResultados(resultados); setImportObservaciones(observaciones); setImportAcidoBase(acidoBase); }; const handleImportarLaboratorio = () => { if (!addAcidoBase) return; if (importResultados.length === 0 && !importObservaciones && !importAcidoBase) return; if (importAcidoBase) { addAcidoBase({ ...importAcidoBase, pacienteId: patientId, internacionId, fecha: importFecha, hora: importHora }); } add({ pacienteId: patientId, fecha: importFecha, hora: importHora, resultados: importResultados, observaciones: importObservaciones }); setImportDialog(false); setImportTexto(''); setImportResultados([]); setImportObservaciones(''); setImportAcidoBase(null); setImportFecha(getLocalToday()); setImportHora(new Date().toTimeString().slice(0, 5)); }; const loadEdit = (l: Laboratorio) => { setEdit(l); setFecha(l.fecha); setHora(l.hora || ''); setObservaciones(l.observaciones || ''); setHto(getValorRaw(l, 'Hematocrito')); setHb(getValorRaw(l, 'Hemoglobina')); setGb(getValorRaw(l, 'Leucocitos')); setPlaq(getValorRaw(l, 'Plaquetas')); setGluc(getValorRaw(l, 'Glucemia')); setUrea(getValorRaw(l, 'Urea')); setCreat(getValorRaw(l, 'Creatinina')); setNa(getValorRaw(l, 'Sodio')); setK(getValorRaw(l, 'Potasio')); setCl(getValorRaw(l, 'Cloro')); setBt(getValorRaw(l, 'Bilirrubina Total')); setBd(getValorRaw(l, 'Bilirrubina Directa')); setGot(getValorRaw(l, 'GOT')); setGpt(getValorRaw(l, 'GPT')); setTp(getValorRaw(l, 'Tiempo de Protrombina')); setKptt(getValorRaw(l, 'KPTT')); setRin(getValorRaw(l, 'INR')); setDialog(true); }; const handleGuardar = () => { const resultadosLaboratorio = buildResultados(); if (edit) { update(edit.id, { fecha, hora, resultados: resultadosLaboratorio, observaciones }); } else { add({ pacienteId: patientId || '', internacionId, fecha, hora, resultados: resultadosLaboratorio, observaciones }); } setDialog(false); reset(); }; const labs = lab.filter(l => l.pacienteId === patientId).sort((a, b) => { const dateA = new Date(a.fecha + 'T' + (a.hora || '00:00')).getTime(); const dateB = new Date(b.fecha + 'T' + (b.hora || '00:00')).getTime(); const diff = dateB - dateA; return labSortAsc ? -diff : diff; }); const labsConObservaciones = lab .filter(l => l.pacienteId === patientId && l.observaciones && l.observaciones.trim() !== '') .sort((a, b) => { const dateA = new Date(a.fecha + 'T' + (a.hora || '00:00')).getTime(); const dateB = new Date(b.fecha + 'T' + (b.hora || '00:00')).getTime(); return dateB - dateA; }); return (
{canEdit && ( { reset(); setDialog(true); }}> Nuevo )} { setParametroEvolucion('Hematocrito'); setEvolDialog(true); }}> Evolución setOtrosDialog(true)}> Ver Otros
{edit ? 'Editar' : 'Nuevo'} Laboratorio
setFecha(e.target.value)} />
setHora(e.target.value)} />

Hemograma

setHto(e.target.value)} />
setHb(e.target.value)} />
setGb(e.target.value)} />
setPlaq(e.target.value)} />

Química

setGluc(e.target.value)} />
setUrea(e.target.value)} />
setCreat(e.target.value)} />

Ionograma

setNa(e.target.value)} />
setK(e.target.value)} />
setCl(e.target.value)} />

Hepatograma

setBt(e.target.value)} />
setBd(e.target.value)} />
setGot(e.target.value)} />
setGpt(e.target.value)} />

Coagulograma

setTp(e.target.value)} />
setKptt(e.target.value)} />
setRin(e.target.value)} />