Agregar FiO2 y edición en gasometrías

- Agregar campo fio2 en interface AcidoBase
- Agregar columna fio2 en SQLite
- Nuevo campo FiO2 en modal nueva gasometría
- Mostrar FiO2 en lista de gasometrías
- Agregar botón edición (lápiz) en registros
- Agregar función actualizarAcidoBase en store
This commit is contained in:
2026-04-16 01:28:31 -03:00
parent 97adfd08b7
commit 0b77297c3d
5 changed files with 84 additions and 8 deletions
+9 -2
View File
@@ -104,6 +104,7 @@ db.exec(`
be REAL,
sato2 REAL,
lactato REAL,
fio2 REAL,
interpretacion TEXT
);
@@ -144,6 +145,12 @@ try {
// La columna ya existe
}
try {
db.exec("ALTER TABLE acidosbase ADD COLUMN fio2 REAL");
} catch (e) {
// La columna ya existe
}
const app = express();
app.use(cors());
app.use(express.json({ limit: '50mb' }));
@@ -254,9 +261,9 @@ app.put('/api/state', (req, res) => {
}
if (state.acidosBase?.length) {
const stmt = db.prepare('INSERT INTO acidosbase (id, pacienteId, fecha, hora, ph, pco2, po2, hco3, be, statO2, lactato, interpretacion) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)');
const stmt = db.prepare('INSERT INTO acidosbase (id, pacienteId, fecha, hora, ph, pco2, po2, hco3, be, sato2, lactato, fio2, interpretacion) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)');
for (const a of state.acidosBase) {
stmt.run(a.id, a.pacienteId, a.fecha, a.hora, a.ph, a.pco2, a.po2, a.hco3, a.be, a.sato2, a.lactato, a.interpretacion);
stmt.run(a.id, a.pacienteId, a.fecha, a.hora, a.ph, a.pco2, a.po2, a.hco3, a.be, a.sato2, a.lactato || null, a.fio2 || null, a.interpretacion);
}
}
+1
View File
@@ -151,6 +151,7 @@ function AppContent() {
onActualizarLaboratorio={store.actualizarLaboratorio}
onEliminarLaboratorio={store.eliminarLaboratorio}
onAgregarAcidoBase={(a) => store.agregarAcidoBase({ ...a, internacionId: internacion.id })}
onActualizarAcidoBase={store.actualizarAcidoBase}
onEliminarAcidoBase={store.eliminarAcidoBase}
onAgregarCultivo={(c) => store.agregarCultivo({ ...c, internacionId: internacion.id })}
onActualizarCultivo={store.actualizarCultivo}
+8
View File
@@ -281,6 +281,13 @@ const finalizarInternacion = useCallback((internacionId: string, datos: {
return nuevoAcidoBase.id;
}, []);
const actualizarAcidoBase = useCallback((id: string, datos: Partial<AcidoBase>) => {
setState(prev => ({
...prev,
acidosBase: prev.acidosBase.map(a => a.id === id ? { ...a, ...datos } : a),
}));
}, []);
const eliminarAcidoBase = useCallback((id: string) => {
setState(prev => ({
...prev,
@@ -457,6 +464,7 @@ const getEstadisticas = useCallback(() => {
agregarLaboratorio,
eliminarLaboratorio,
agregarAcidoBase,
actualizarAcidoBase,
eliminarAcidoBase,
agregarCultivo,
actualizarCultivo,
+65 -6
View File
@@ -59,6 +59,7 @@ interface HistoriaClinicaProps {
onActualizarLaboratorio: (id: string, datos: Partial<Laboratorio>) => void;
onEliminarLaboratorio: (id: string) => void;
onAgregarAcidoBase: (acidoBase: Omit<AcidoBase, 'id'>) => void;
onActualizarAcidoBase: (id: string, datos: Partial<AcidoBase>) => void;
onEliminarAcidoBase: (id: string) => void;
onAgregarCultivo: (cultivo: Omit<Cultivo, 'id'>) => void;
onActualizarCultivo: (id: string, datos: Partial<Cultivo>) => void;
@@ -159,6 +160,7 @@ export function HistoriaClinica({
onActualizarLaboratorio,
onEliminarLaboratorio,
onAgregarAcidoBase,
onActualizarAcidoBase,
onEliminarAcidoBase,
onAgregarCultivo,
onActualizarCultivo,
@@ -532,7 +534,7 @@ export function HistoriaClinica({
</TabsContent>
<TabsContent value="acidobase" className="mt-4">
<SeccionAcidosBase ab={acidosBase} patientId={paciente.id} add={onAgregarAcidoBase} del={onEliminarAcidoBase} />
<SeccionAcidosBase ab={acidosBase} patientId={paciente.id} add={onAgregarAcidoBase} update={onActualizarAcidoBase} del={onEliminarAcidoBase} />
</TabsContent>
<TabsContent value="cultivos" className="mt-4">
@@ -1295,13 +1297,16 @@ function SeccionEvoluciones({ evos, internacionId, add, update, del }: {
);
}
function SeccionAcidosBase({ ab, patientId, add, del }: {
function SeccionAcidosBase({ ab, patientId, add, update, del }: {
ab: AcidoBase[];
patientId: string;
add: (a: Omit<AcidoBase, 'id'>) => void;
update: (id: string, datos: Partial<AcidoBase>) => void;
del: (id: string) => void;
}) {
const [dialog, setDialog] = useState(false);
const [editDialog, setEditDialog] = useState(false);
const [selected, setSelected] = useState<AcidoBase | null>(null);
const [fecha, setFecha] = useState(new Date().toISOString().split('T')[0]);
const [hora, setHora] = useState(new Date().toTimeString().slice(0, 5));
const [ph, setPh] = useState('');
@@ -1311,12 +1316,30 @@ function SeccionAcidosBase({ ab, patientId, add, del }: {
const [be, setBe] = useState('');
const [sato2, setSato2] = useState('');
const [lactato, setLactato] = useState('');
const [fio2, setFio2] = useState('');
const [interpretacion, setInterpretacion] = useState('');
const reset = () => {
setFecha(new Date().toISOString().split('T')[0]);
setHora(new Date().toTimeString().slice(0, 5));
setPh(''); setPco2(''); setPo2(''); setHco3(''); setBe(''); setSato2(''); setLactato(''); setInterpretacion('');
setPh(''); setPco2(''); setPo2(''); setHco3(''); setBe(''); setSato2(''); setLactato(''); setFio2(''); setInterpretacion('');
setSelected(null);
};
const loadEdit = (a: AcidoBase) => {
setSelected(a);
setFecha(a.fecha);
setHora(a.hora);
setPh(String(a.ph));
setPco2(String(a.pco2));
setPo2(String(a.po2));
setHco3(String(a.hco3));
setBe(String(a.be));
setSato2(String(a.sato2));
setLactato(a.lactato ? String(a.lactato) : '');
setFio2(a.fio2 ? String(a.fio2) : '');
setInterpretacion(a.interpretacion || '');
setEditDialog(true);
};
const abFiltered = ab.filter(a => a.pacienteId === patientId).sort((a, b) => new Date(b.fecha + 'T' + b.hora).getTime() - new Date(a.fecha + 'T' + a.hora).getTime());
@@ -1350,11 +1373,19 @@ function SeccionAcidosBase({ ab, patientId, add, del }: {
const handle = () => {
if (!ph || !pco2 || !po2 || !hco3 || !be || !sato2) return;
const interpretacionAuto = interpretarGasometria();
add({ pacienteId: patientId, fecha, hora, ph: parseFloat(ph), pco2: parseFloat(pco2), po2: parseFloat(po2), hco3: parseFloat(hco3), be: parseFloat(be), sato2: parseFloat(sato2), lactato: lactato ? parseFloat(lactato) : undefined, interpretacion: interpretacion || interpretacionAuto });
add({ pacienteId: patientId, fecha, hora, ph: parseFloat(ph), pco2: parseFloat(pco2), po2: parseFloat(po2), hco3: parseFloat(hco3), be: parseFloat(be), sato2: parseFloat(sato2), lactato: lactato ? parseFloat(lactato) : undefined, fio2: fio2 ? parseFloat(fio2) : undefined, interpretacion: interpretacion || interpretacionAuto });
setDialog(false);
reset();
};
const handleEdit = () => {
if (!selected || !ph || !pco2 || !po2 || !hco3 || !be || !sato2) return;
const interpretacionAuto = interpretarGasometria();
update(selected.id, { fecha, hora, ph: parseFloat(ph), pco2: parseFloat(pco2), po2: parseFloat(po2), hco3: parseFloat(hco3), be: parseFloat(be), sato2: parseFloat(sato2), lactato: lactato ? parseFloat(lactato) : undefined, fio2: fio2 ? parseFloat(fio2) : undefined, interpretacion: interpretacion || interpretacionAuto });
setEditDialog(false);
reset();
};
return (
<div className="space-y-4">
<div className="flex">
@@ -1368,7 +1399,7 @@ function SeccionAcidosBase({ ab, patientId, add, del }: {
<div><Label>Fecha *</Label><Input type="date" value={fecha} onChange={e => setFecha(e.target.value)} /></div>
<div><Label>Hora *</Label><Input type="time" value={hora} onChange={e => setHora(e.target.value)} /></div>
</div>
<div className="grid grid-cols-7 gap-2">
<div className="grid grid-cols-4 sm:grid-cols-8 gap-2">
<div><Label>pH *</Label><Input type="number" step="0.01" value={ph} onChange={e => setPh(e.target.value)} placeholder="7.40" /></div>
<div><Label>pCO2 *</Label><Input type="number" value={pco2} onChange={e => setPco2(e.target.value)} placeholder="40" /></div>
<div><Label>pO2 *</Label><Input type="number" value={po2} onChange={e => setPo2(e.target.value)} placeholder="85" /></div>
@@ -1376,6 +1407,7 @@ function SeccionAcidosBase({ ab, patientId, add, del }: {
<div><Label>BE *</Label><Input type="number" step="0.1" value={be} onChange={e => setBe(e.target.value)} placeholder="0" /></div>
<div><Label>SatO2 *</Label><Input type="number" value={sato2} onChange={e => setSato2(e.target.value)} placeholder="97" /></div>
<div><Label>Lactato</Label><Input type="number" step="0.1" value={lactato} onChange={e => setLactato(e.target.value)} placeholder="1.0" /></div>
<div><Label>FiO2</Label><Input type="number" value={fio2} onChange={e => setFio2(e.target.value)} placeholder="21" /></div>
</div>
{ph && pco2 && hco3 && <div className="bg-blue-50 p-3 rounded-lg border border-blue-200"><p className="text-sm font-medium text-blue-800 flex items-center gap-2"><Activity className="h-4 w-4" />Interpretación automática: {interpretarGasometria()}</p></div>}
<div><Label>Interpretación / Comentarios</Label><textarea className="w-full p-2 border rounded-md text-sm min-h-[80px]" value={interpretacion} onChange={e => setInterpretacion(e.target.value)} placeholder="Interpretación clínica..." /></div>
@@ -1383,6 +1415,30 @@ function SeccionAcidosBase({ ab, patientId, add, del }: {
<div className="flex justify-end gap-2 mt-4"><Button variant="outline" onClick={() => setDialog(false)}><X className="h-4 w-4 mr-2" />Cancelar</Button><Button onClick={handle} disabled={!ph || !pco2 || !po2 || !hco3 || !be || !sato2}><Plus className="h-4 w-4 mr-2" />Guardar Gasometría</Button></div>
</DialogContent>
</Dialog>
<Dialog open={editDialog} onOpenChange={setEditDialog}>
<DialogContent className="sm:max-w-2xl max-h-[90vh] overflow-y-auto">
<DialogHeader><DialogTitle>Editar Gasometría Arterial</DialogTitle></DialogHeader>
<div className="space-y-4">
<div className="grid grid-cols-2 gap-4">
<div><Label>Fecha *</Label><Input type="date" value={fecha} onChange={e => setFecha(e.target.value)} /></div>
<div><Label>Hora *</Label><Input type="time" value={hora} onChange={e => setHora(e.target.value)} /></div>
</div>
<div className="grid grid-cols-4 sm:grid-cols-8 gap-2">
<div><Label>pH *</Label><Input type="number" step="0.01" value={ph} onChange={e => setPh(e.target.value)} placeholder="7.40" /></div>
<div><Label>pCO2 *</Label><Input type="number" value={pco2} onChange={e => setPco2(e.target.value)} placeholder="40" /></div>
<div><Label>pO2 *</Label><Input type="number" value={po2} onChange={e => setPo2(e.target.value)} placeholder="85" /></div>
<div><Label>HCO3 *</Label><Input type="number" step="0.1" value={hco3} onChange={e => setHco3(e.target.value)} placeholder="24" /></div>
<div><Label>BE *</Label><Input type="number" step="0.1" value={be} onChange={e => setBe(e.target.value)} placeholder="0" /></div>
<div><Label>SatO2 *</Label><Input type="number" value={sato2} onChange={e => setSato2(e.target.value)} placeholder="97" /></div>
<div><Label>Lactato</Label><Input type="number" step="0.1" value={lactato} onChange={e => setLactato(e.target.value)} placeholder="1.0" /></div>
<div><Label>FiO2</Label><Input type="number" value={fio2} onChange={e => setFio2(e.target.value)} placeholder="21" /></div>
</div>
{ph && pco2 && hco3 && <div className="bg-blue-50 p-3 rounded-lg border border-blue-200"><p className="text-sm font-medium text-blue-800 flex items-center gap-2"><Activity className="h-4 w-4" />Interpretación automática: {interpretarGasometria()}</p></div>}
<div><Label>Interpretación / Comentarios</Label><textarea className="w-full p-2 border rounded-md text-sm min-h-[80px]" value={interpretacion} onChange={e => setInterpretacion(e.target.value)} placeholder="Interpretación clínica..." /></div>
</div>
<div className="flex justify-end gap-2 mt-4"><Button variant="outline" onClick={() => setEditDialog(false)}><X className="h-4 w-4 mr-2" />Cancelar</Button><Button onClick={handleEdit} disabled={!ph || !pco2 || !po2 || !hco3 || !be || !sato2}><Save className="h-4 w-4 mr-2" />Guardar Cambios</Button></div>
</DialogContent>
</Dialog>
{abFiltered.length === 0 ? <div className="text-center py-8 text-gray-400"><Activity className="h-12 w-12 mx-auto mb-2 opacity-50" /><p>No hay gasometrías</p></div> :
abFiltered.map(a => {
const phSt = getEstadoParametro('ph', a.ph);
@@ -1392,11 +1448,13 @@ function SeccionAcidosBase({ ab, patientId, add, del }: {
<div className="flex items-start justify-between">
<div><p className="font-medium">{a.fecha} {a.hora}</p></div>
<div className="flex items-center gap-2">
<Badge variant="outline" className="text-xs">{a.fio2 && <span className="mr-1">FiO:{a.fio2}</span>}</Badge>
<Badge className={getColorPh(a.ph)}>pH: {a.ph}</Badge>
<Button size="sm" variant="ghost" className="text-blue-600" onClick={() => loadEdit(a)}><Edit /></Button>
<Button size="sm" variant="ghost" className="text-red-600" onClick={() => del(a.id)}><Trash2 /></Button>
</div>
</div>
<div className="grid grid-cols-3 sm:grid-cols-7 gap-2">
<div className="grid grid-cols-3 sm:grid-cols-8 gap-2">
<div className="bg-gray-50 p-2 rounded text-center"><p className="text-xs text-gray-500">pH</p><p className={`font-bold ${phSt.color}`}>{a.ph}</p></div>
<div className="bg-gray-50 p-2 rounded text-center"><p className="text-xs text-gray-500">pCO2</p><p className="font-bold">{a.pco2}</p></div>
<div className="bg-gray-50 p-2 rounded text-center"><p className="text-xs text-gray-500">pO2</p><p className="font-bold">{a.po2}</p></div>
@@ -1404,6 +1462,7 @@ function SeccionAcidosBase({ ab, patientId, add, del }: {
<div className="bg-gray-50 p-2 rounded text-center"><p className="text-xs text-gray-500">BE</p><p className="font-bold">{a.be}</p></div>
<div className="bg-gray-50 p-2 rounded text-center"><p className="text-xs text-gray-500">SatO2</p><p className="font-bold">{a.sato2}%</p></div>
{a.lactato && <div className="bg-gray-50 p-2 rounded text-center"><p className="text-xs text-gray-500">Lactato</p><p className="font-bold">{a.lactato}</p></div>}
{a.fio2 && <div className="bg-gray-50 p-2 rounded text-center"><p className="text-xs text-gray-500">FiO2</p><p className="font-bold">{a.fio2}%</p></div>}
</div>
{a.interpretacion && <div className="bg-teal-50 p-3 rounded-lg border border-teal-200"><p className="text-sm font-medium text-teal-800">Interpretación: {a.interpretacion}</p></div>}
</div>
+1
View File
@@ -123,6 +123,7 @@ export interface AcidoBase {
be: number;
sato2: number;
lactato?: number;
fio2?: number;
interpretacion?: string;
}