feat: implement tracking for medical indication changes with a new database table and management hooks

This commit is contained in:
2026-04-21 22:18:51 -03:00
parent b7d207bf01
commit e1b2e907ca
6 changed files with 216 additions and 69 deletions
+38 -8
View File
@@ -251,33 +251,54 @@ try {
// column may already exist // column may already exist
} }
try { try {
db.exec("ALTER TABLE recomendaciones ADD COLUMN fechaModificacion TEXT"); db.exec("ALTER TABLE indicaciones ADD COLUMN fechaModificacion TEXT");
} catch (e) { } catch (e) {
// column may already exist // column may already exist
} }
try { try {
db.exec("ALTER TABLE recomendaciones ADD COLUMN tipoInsulina TEXT"); db.exec("ALTER TABLE indicaciones ADD COLUMN tipoInsulina TEXT");
} catch (e) { } catch (e) {
// column may already exist // column may already exist
} }
try { try {
db.exec("ALTER TABLE recomendaciones ADD COLUMN unidadesDesayuno INTEGER"); db.exec("ALTER TABLE indicaciones ADD COLUMN unidadesDesayuno INTEGER");
} catch (e) { } catch (e) {
// column may already exist // column may already exist
} }
try { try {
db.exec("ALTER TABLE recomendaciones ADD COLUMN unidadesAlmuerzo INTEGER"); db.exec("ALTER TABLE indicaciones ADD COLUMN unidadesAlmuerzo INTEGER");
} catch (e) { } catch (e) {
// column may already exist // column may already exist
} }
try { try {
db.exec("ALTER TABLE recomendaciones ADD COLUMN unidadesNoche INTEGER"); db.exec("ALTER TABLE indicaciones ADD COLUMN unidadesNoche INTEGER");
} catch (e) { } catch (e) {
// column may already exist // column may already exist
} }
} }
} catch (e) { } catch (e) {
console.log('Error tabla recomendaciones:', e.message); console.log('Error tabla indicaciones:', e.message);
}
try {
const exists = db.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='movimientos_indicaciones'").get();
if (!exists) {
db.exec(`
CREATE TABLE movimientos_indicaciones (
id TEXT PRIMARY KEY,
indicacionId TEXT NOT NULL,
internacionId TEXT NOT NULL,
tipo TEXT NOT NULL,
fecha TEXT NOT NULL,
profesional TEXT NOT NULL,
indicacionPrevia TEXT,
indicacionNueva TEXT
)
`);
console.log('Tabla movimientos_indicaciones creada');
}
} catch (e) {
console.log('Tabla movimientos_indicaciones:', e.message);
} }
const app = express(); const app = express();
@@ -332,6 +353,7 @@ app.get('/api/state', (req, res) => {
interconsultas: queryAll('SELECT * FROM interconsultas').map(ic => ({ ...ic, realizada: !!ic.realizada })), interconsultas: queryAll('SELECT * FROM interconsultas').map(ic => ({ ...ic, realizada: !!ic.realizada })),
atb: queryAll('SELECT * FROM atb'), atb: queryAll('SELECT * FROM atb'),
indicaciones: queryAll('SELECT * FROM indicaciones'), indicaciones: queryAll('SELECT * FROM indicaciones'),
movimientosIndicaciones: queryAll('SELECT * FROM movimientos_indicaciones'),
vistaActual: 'dashboard', vistaActual: 'dashboard',
currentInternacionId: null currentInternacionId: null
}; };
@@ -432,9 +454,17 @@ if (state.laboratorios?.length) {
run('DELETE FROM indicaciones'); run('DELETE FROM indicaciones');
if (state.indicaciones?.length) { if (state.indicaciones?.length) {
const stmt = db.prepare('INSERT INTO indicaciones (id, internacionId, tipo, droga, dosis, frecuenciaHoras, via, descripcion, tipoPlan, tipoPlan2, cantidadMl, cantidadMl2, tiempoHoras, estado, medicoCrea, fechaCrea, medicoModifica, fechaModificacion, medicoSuspende, fechaSuspension) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)'); const stmt = db.prepare('INSERT INTO recomendaciones (id, internacionId, tipo, droga, dosis, frecuenciaHoras, via, descripcion, tipoPlan, tipoPlan2, cantidadMl, cantidadMl2, tiempoHoras, estado, medicoCrea, fechaCrea, medicoModifica, medicoSuspende, fechaSuspension, tipoInsulina, unidadesDesayuno, unidadesAlmuerzo, unidadesNoche) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)');
for (const ind of state.indicaciones) { for (const ind of state.indicaciones) {
stmt.run(ind.id, ind.internacionId, ind.tipo, ind.droga || null, ind.dosis || null, ind.frecuenciaHoras || null, ind.via || null, ind.descripcion || null, ind.tipoPlan || null, ind.tipoPlan2 || null, ind.cantidadMl || null, ind.cantidadMl2 || null, ind.tiempoHoras || null, ind.estado, ind.medicoCrea, ind.fechaCrea, ind.medicoModifica || null, ind.fechaModificacion || null, ind.medicoSuspende || null, ind.fechaSuspension || null); stmt.run(ind.id, ind.internacionId, ind.tipo, ind.droga || null, ind.dosis || null, ind.frecuenciaHoras || null, ind.via || null, ind.descripcion || null, ind.tipoPlan || null, ind.tipoPlan2 || null, ind.cantidadMl || null, ind.cantidadMl2 || null, ind.tiempoHoras || null, ind.estado, ind.medicoCrea, ind.fechaCrea, ind.medicoModifica || null, ind.medicoSuspende || null, ind.fechaSuspension || null, ind.tipoInsulina || null, ind.unidadesDesayuno || null, ind.unidadesAlmuerzo || null, ind.unidadesNoche || null);
}
}
run('DELETE FROM movimientos_indicaciones');
if (state.movimientosIndicaciones?.length) {
const stmt = db.prepare('INSERT INTO movimientos_indicaciones (id, indicacionId, internacionId, tipo, fecha, profesional, indicacionPrevia, indicacionNueva) VALUES (?, ?, ?, ?, ?, ?, ?, ?)');
for (const mov of state.movimientosIndicaciones) {
stmt.run(mov.id, mov.indicacionId, mov.internacionId, mov.tipo, mov.fecha, mov.profesional, mov.indicacionPrevia || null, mov.indicacionNueva || null);
} }
} }
+2
View File
@@ -168,9 +168,11 @@ function AppContent() {
onActualizarATB={store.actualizarATB} onActualizarATB={store.actualizarATB}
onEliminarATB={store.eliminarATB} onEliminarATB={store.eliminarATB}
indicadores={(Array.isArray(store.indicaciones) ? store.indicaciones : []).filter((i: any) => i.internacionId === internacion.id)} indicadores={(Array.isArray(store.indicaciones) ? store.indicaciones : []).filter((i: any) => i.internacionId === internacion.id)}
movimientos={store.getMovimientosByInternacion ? store.getMovimientosByInternacion(internacion.id) : []}
onAgregarIndicacion={(i) => store.agregarIndicacion({ ...i, internacionId: internacion.id })} onAgregarIndicacion={(i) => store.agregarIndicacion({ ...i, internacionId: internacion.id })}
onActualizarIndicacion={store.actualizarIndicacion} onActualizarIndicacion={store.actualizarIndicacion}
onEliminarIndicacion={store.eliminarIndicacion} onEliminarIndicacion={store.eliminarIndicacion}
onAgregarMovimiento={(m: any) => store.agregarMovimientoIndicacion ? store.agregarMovimientoIndicacion({ ...m, internacionId: internacion.id }) : null}
onActualizarInternacion={store.actualizarInternacion} onActualizarInternacion={store.actualizarInternacion}
onActualizarCama={store.actualizarCama} onActualizarCama={store.actualizarCama}
onVolver={() => store.setVista('internaciones')} onVolver={() => store.setVista('internaciones')}
+20 -1
View File
@@ -41,6 +41,7 @@ interface HospitalState {
interconsultas: Interconsulta[]; interconsultas: Interconsulta[];
atb: ATB[]; atb: ATB[];
indicaciones: Indicacion[]; indicaciones: Indicacion[];
movimientosIndicaciones: any[];
vistaActual: Vista; vistaActual: Vista;
currentInternacionId?: string | null; currentInternacionId?: string | null;
} }
@@ -61,6 +62,7 @@ const defaultState = (): HospitalState => ({
atb: [], atb: [],
vistaActual: 'dashboard', vistaActual: 'dashboard',
indicaciones: [], indicaciones: [],
movimientosIndicaciones: [],
camas: [] camas: []
}); });
@@ -77,7 +79,9 @@ export function useHospitalStore() {
if (!res.ok) throw new Error('no state'); if (!res.ok) throw new Error('no state');
const body = await res.json(); const body = await res.json();
if (mounted && body) { if (mounted && body) {
setState(body as HospitalState); const normalized = body as HospitalState;
normalized.movimientosIndicaciones = normalized.movimientosIndicaciones || [];
setState(normalized);
} }
} catch (err) { } catch (err) {
// no remote state or unreachable — keep defaults // no remote state or unreachable — keep defaults
@@ -428,6 +432,19 @@ const finalizarInternacion = useCallback((internacionId: string, datos: {
})); }));
}, []); }, []);
const agregarMovimientoIndicacion = useCallback((movimiento: any) => {
setState(prev => ({
...prev,
movimientosIndicaciones: [...(prev.movimientosIndicaciones || []), { ...movimiento, id: generateUUID() }],
}));
}, []);
const getMovimientosByInternacion = useCallback((internacionId: string) => {
return (state.movimientosIndicaciones || [])
.filter((m: any) => m.internacionId === internacionId)
.sort((a: any, b: any) => new Date(b.fecha).getTime() - new Date(a.fecha).getTime());
}, [state.movimientosIndicaciones]);
// Acciones de areas // Acciones de areas
const agregarArea = useCallback((area: Omit<Area, 'id'>) => { const agregarArea = useCallback((area: Omit<Area, 'id'>) => {
const nuevaArea: Area = { ...area, id: generateUUID() }; const nuevaArea: Area = { ...area, id: generateUUID() };
@@ -560,6 +577,8 @@ const getEstadisticas = useCallback(() => {
agregarIndicacion, agregarIndicacion,
actualizarIndicacion, actualizarIndicacion,
eliminarIndicacion, eliminarIndicacion,
agregarMovimientoIndicacion,
getMovimientosByInternacion,
agregarArea, agregarArea,
actualizarArea, actualizarArea,
eliminarArea, eliminarArea,
+6
View File
@@ -93,6 +93,8 @@ interface HistoriaClinicaProps {
onAgregarIndicacion: (indicacion: Omit<Indicacion, 'id'>) => void; onAgregarIndicacion: (indicacion: Omit<Indicacion, 'id'>) => void;
onActualizarIndicacion: (id: string, datos: Partial<Indicacion>) => void; onActualizarIndicacion: (id: string, datos: Partial<Indicacion>) => void;
onEliminarIndicacion: (id: string) => void; onEliminarIndicacion: (id: string) => void;
movimientos?: any[];
onAgregarMovimiento?: (m: any) => void;
onActualizarInternacion: (id: string, datos: Partial<Internacion>) => void; onActualizarInternacion: (id: string, datos: Partial<Internacion>) => void;
onActualizarCama: (id: string, datos: Partial<Cama>) => void; onActualizarCama: (id: string, datos: Partial<Cama>) => void;
onVolver: () => void; onVolver: () => void;
@@ -209,6 +211,8 @@ export function HistoriaClinica({
onAgregarIndicacion, onAgregarIndicacion,
onActualizarIndicacion, onActualizarIndicacion,
onEliminarIndicacion, onEliminarIndicacion,
movimientos,
onAgregarMovimiento,
onActualizarInternacion, onActualizarInternacion,
onActualizarCama, onActualizarCama,
onVolver, onVolver,
@@ -559,6 +563,8 @@ export function HistoriaClinica({
add={onAgregarIndicacion} add={onAgregarIndicacion}
update={onActualizarIndicacion} update={onActualizarIndicacion}
del={onEliminarIndicacion} del={onEliminarIndicacion}
movimientos={movimientos}
onAgregarMovimiento={onAgregarMovimiento}
/> />
</TabsContent> </TabsContent>
+135 -45
View File
@@ -10,17 +10,50 @@ import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@
import { Plus, Pencil, Trash2, X } from 'lucide-react'; import { Plus, Pencil, Trash2, X } from 'lucide-react';
import type { Indicacion, IndicacionTipo, ViaAdministracion, TipoPlanHidratacion, TipoInsulina } from '@/types'; import type { Indicacion, IndicacionTipo, ViaAdministracion, TipoPlanHidratacion, TipoInsulina } from '@/types';
export function SeccionIndicaciones({ recomendaciones, internacionId, add, update, del }: { export function SeccionIndicaciones({ recomendaciones, internacionId, add, update, del, movimientos, onAgregarMovimiento }: {
recomendaciones: Indicacion[]; recomendaciones: Indicacion[];
internacionId: string; internacionId: string;
add: (i: Omit<Indicacion, 'id'>) => void; add: (i: Omit<Indicacion, 'id'>) => void;
update: (id: string, datos: Partial<Indicacion>) => void; update: (id: string, datos: Partial<Indicacion>) => void;
del: (id: string) => void; del: (id: string) => void;
movimientos?: any[];
onAgregarMovimiento?: (m: any) => void;
}) { }) {
const list: Indicacion[] = Array.isArray(recomendaciones) ? recomendaciones : []; const list: Indicacion[] = Array.isArray(recomendaciones) ? recomendaciones : [];
const movs: any[] = Array.isArray(movimientos) ? movimientos : [];
const [dialog, setDialog] = useState(false); const [dialog, setDialog] = useState(false);
const [edit, setEdit] = useState<Indicacion | null>(null); const [edit, setEdit] = useState<Indicacion | null>(null);
const formatIndicacion = (i: Indicacion): string => {
if (i.tipo === 'Farmacologica') return `${i.droga || ''} ${i.dosis || ''} ${i.frecuenciaHoras ? `c/${i.frecuenciaHoras}hs` : ''} ${i.via || ''}`.trim();
if (i.tipo === 'Farmacologica Insulina') return `${i.tipoInsulina || ''}${i.unidadesDesayuno ? ` - Desayuno: ${i.unidadesDesayuno}U` : ''}${i.unidadesAlmuerzo ? ` - Almuerzo: ${i.unidadesAlmuerzo}U` : ''}${i.unidadesNoche ? ` - 23hs: ${i.unidadesNoche}U` : ''}`.trim();
if (i.tipo === 'No Farmacologica') return i.descripcion || '';
if (i.tipo === 'PHP' || i.tipo === 'PHP Paralelo') return `${i.tipoPlan || ''} ${i.cantidadMl || ''}ml en ${i.tiempoHoras || ''}hs`;
if (i.tipo === 'PHP Alterno') return `${i.tipoPlan || ''} ${i.cantidadMl || ''}ml + ${i.tipoPlan2 || ''} ${i.cantidadMl2 || ''}ml en ${i.tiempoHoras || ''}hs`;
return '';
};
const [tipo, setTipo] = useState<IndicacionTipo>('Farmacologica'); const [tipo, setTipo] = useState<IndicacionTipo>('Farmacologica');
const [sortField, setSortField] = useState<'fecha' | 'profesional' | 'tipo'>('fecha');
const [sortDir, setSortDir] = useState<'asc' | 'desc'>('desc');
const handleSort = (field: 'fecha' | 'profesional' | 'tipo') => {
if (sortField === field) {
setSortDir(sortDir === 'asc' ? 'desc' : 'asc');
} else {
setSortField(field);
setSortDir('desc');
}
};
const sortedMovs = [...movs].sort((a, b) => {
let aVal: any = a[sortField] || '';
let bVal: any = b[sortField] || '';
if (sortField === 'fecha') {
aVal = new Date(aVal.replace(' ', 'T')).getTime();
bVal = new Date(bVal.replace(' ', 'T')).getTime();
}
return sortDir === 'asc' ? (aVal > bVal ? 1 : -1) : (aVal < bVal ? 1 : -1);
});
const [droga, setDroga] = useState(''); const [droga, setDroga] = useState('');
const [dosis, setDosis] = useState(''); const [dosis, setDosis] = useState('');
const [frecuenciaHoras, setFrecuenciaHoras] = useState<number | ''>(''); const [frecuenciaHoras, setFrecuenciaHoras] = useState<number | ''>('');
@@ -38,10 +71,10 @@ export function SeccionIndicaciones({ recomendaciones, internacionId, add, updat
const [medico, setMedico] = useState(''); const [medico, setMedico] = useState('');
const [showHistorial, setShowHistorial] = useState(false); const [showHistorial, setShowHistorial] = useState(false);
const vias: ViaAdministracion[] = ['Oral', 'EV', 'IM', 'SC', 'Por GGT', 'Por SNG']; const vias: ViaAdministracion[] = ['Via Oral', 'EV', 'IM', 'SC', 'Por GGT', 'Por SNG'];
const planes: TipoPlanHidratacion[] = ['SF 0.9%', 'Dextrosa 5%', 'Dextrosa 10%', 'Dextrosa 25%', 'Ringer Lactato']; const planes: TipoPlanHidratacion[] = ['SF 0.9%', 'Dextrosa 5%', 'Dextrosa 10%', 'Dextrosa 25%', 'Ringer Lactato'];
const tiposInsulina: TipoInsulina[] = ['NPH', 'Glargina']; const tiposInsulina: TipoInsulina[] = ['NPH', 'Glargina'];
const tipos: IndicacionTipo[] = ['Farmacologica', 'Farmacologica Insulina', 'No Farmacologica', 'PHP', 'Paralelo', 'Alterno']; const tipos: IndicacionTipo[] = ['Farmacologica', 'Farmacologica Profilactica', 'Farmacologica Insulina', 'No Farmacologica', 'PHP', 'PHP Paralelo', 'PHP Alterno'];
const reset = () => { const reset = () => {
setEdit(null); setEdit(null);
@@ -86,11 +119,11 @@ export function SeccionIndicaciones({ recomendaciones, internacionId, add, updat
const handleGuardar = () => { const handleGuardar = () => {
if (!medico.trim()) return; if (!medico.trim()) return;
if (tipo === 'Farmacologica' && (!droga.trim() || !dosis.trim())) return; if ((tipo === 'Farmacologica' || tipo === 'Farmacologica Profilactica') && (!droga.trim() || !dosis.trim())) return;
if (tipo === 'Farmacologica Insulina' && (!unidadesDesayuno && !unidadesAlmuerzo && !unidadesNoche)) return; if (tipo === 'Farmacologica Insulina' && (!unidadesDesayuno && !unidadesAlmuerzo && !unidadesNoche)) return;
if (tipo === 'No Farmacologica' && !descripcion.trim()) return; if (tipo === 'No Farmacologica' && !descripcion.trim()) return;
if ((tipo === 'PHP' || tipo === 'Paralelo') && !cantidadMl) return; if ((tipo === 'PHP' || tipo === 'PHP Paralelo') && !cantidadMl) return;
if (tipo === 'Alterno' && (!cantidadMl || !cantidadMl2)) return; if (tipo === 'PHP Alterno' && (!cantidadMl || !cantidadMl2)) return;
const data: any = { const data: any = {
internacionId, internacionId,
@@ -100,7 +133,7 @@ export function SeccionIndicaciones({ recomendaciones, internacionId, add, updat
fechaCrea: new Date().toISOString().split('T')[0], fechaCrea: new Date().toISOString().split('T')[0],
}; };
if (tipo === 'Farmacologica') { if (tipo === 'Farmacologica' || tipo === 'Farmacologica Profilactica') {
data.droga = droga; data.droga = droga;
data.dosis = dosis; data.dosis = dosis;
data.frecuenciaHoras = frecuenciaHoras || undefined; data.frecuenciaHoras = frecuenciaHoras || undefined;
@@ -110,13 +143,14 @@ export function SeccionIndicaciones({ recomendaciones, internacionId, add, updat
data.unidadesDesayuno = unidadesDesayuno || undefined; data.unidadesDesayuno = unidadesDesayuno || undefined;
data.unidadesAlmuerzo = unidadesAlmuerzo || undefined; data.unidadesAlmuerzo = unidadesAlmuerzo || undefined;
data.unidadesNoche = unidadesNoche || undefined; data.unidadesNoche = unidadesNoche || undefined;
console.log('Guardando insulina:', tipoInsulina, unidadesDesayuno, unidadesAlmuerzo, unidadesNoche);
} else if (tipo === 'No Farmacologica') { } else if (tipo === 'No Farmacologica') {
data.descripcion = descripcion; data.descripcion = descripcion;
} else if (tipo === 'PHP' || tipo === 'Paralelo') { } else if (tipo === 'PHP' || tipo === 'PHP Paralelo') {
data.tipoPlan = tipoPlan; data.tipoPlan = tipoPlan;
data.cantidadMl = cantidadMl; data.cantidadMl = cantidadMl;
data.tiempoHoras = tiempoHoras; data.tiempoHoras = tiempoHoras;
} else if (tipo === 'Alterno') { } else if (tipo === 'PHP Alterno') {
data.tipoPlan = tipoPlan; data.tipoPlan = tipoPlan;
data.cantidadMl = cantidadMl; data.cantidadMl = cantidadMl;
data.tipoPlan2 = tipoPlan2; data.tipoPlan2 = tipoPlan2;
@@ -124,27 +158,61 @@ export function SeccionIndicaciones({ recomendaciones, internacionId, add, updat
data.tiempoHoras = tiempoHoras; data.tiempoHoras = tiempoHoras;
} }
const now = new Date();
const fecha = now.toISOString().split('T')[0] + ' ' + now.toTimeString().split(' ')[0].substring(0, 5);
if (edit) { if (edit) {
if (onAgregarMovimiento) {
onAgregarMovimiento({
indicacionId: edit.id,
internacionId,
tipo: 'Modificacion',
fecha,
profesional: medico,
indicacionPrevia: formatIndicacion(edit),
indicacionNueva: formatIndicacion({ ...edit, ...data } as Indicacion),
});
}
update(edit.id, data); update(edit.id, data);
} else { } else {
add(data); const newId = add(data);
if (onAgregarMovimiento && newId) {
onAgregarMovimiento({
indicacionId: newId,
internacionId,
tipo: 'Indicacion',
fecha,
profesional: medico,
indicacionNueva: formatIndicacion({ ...data, id: newId } as Indicacion),
});
}
} }
setDialog(false); setDialog(false);
reset(); reset();
}; };
const handleSuspender = (i: Indicacion, suspendioMedico: string) => { const handleSuspender = (i: Indicacion, suspendioMedico: string) => {
const now = new Date();
const fecha = now.toISOString().split('T')[0] + ' ' + now.toTimeString().split(' ')[0].substring(0, 5);
update(i.id, { update(i.id, {
estado: 'Suspendida', estado: 'Suspendida',
medicoSuspende: suspendioMedico, medicoSuspende: suspendioMedico,
fechaSuspension: new Date().toISOString().split('T')[0], fechaSuspension: fecha,
}); });
if (onAgregarMovimiento) {
onAgregarMovimiento({
indicacionId: i.id,
internacionId,
tipo: 'Suspencion',
fecha,
profesional: suspendioMedico,
indicacionPrevia: formatIndicacion(i),
});
}
}; };
const handleModificar = (i: Indicacion, modificoMedico: string) => { const handleModificar = (i: Indicacion, modificoMedico: string) => {
update(i.id, { update(i.id, {
medicoModifica: modificoMedico, medicoModifica: modificoMedico,
fechaModificacion: new Date().toISOString().split('T')[0],
}); });
}; };
@@ -159,7 +227,7 @@ export function SeccionIndicaciones({ recomendaciones, internacionId, add, updat
<Plus className="h-4 w-4 mr-2" />Nueva Indicación <Plus className="h-4 w-4 mr-2" />Nueva Indicación
</Button> </Button>
<Button variant="ghost" onClick={() => setShowHistorial(!showHistorial)}> <Button variant="ghost" onClick={() => setShowHistorial(!showHistorial)}>
{showHistorial ? 'Ocultar Historial' : `Ver Historial (${historial.length})`} {showHistorial ? 'Ocultar Historial' : `Ver Historial (${movs.length})`}
</Button> </Button>
</div> </div>
@@ -184,7 +252,17 @@ export function SeccionIndicaciones({ recomendaciones, internacionId, add, updat
<Input value={medico} onChange={e => setMedico(e.target.value)} placeholder="Nombre del médico" /> <Input value={medico} onChange={e => setMedico(e.target.value)} placeholder="Nombre del médico" />
</div> </div>
{tipo === 'Farmacologica' && ( {tipo === 'Farmacologica' && (
<>
<div><Label>Droga *</Label><Input value={droga} onChange={e => setDroga(e.target.value)} placeholder="Nombre del medicamento" /></div>
<div><Label>Dosis *</Label><Input value={dosis} onChange={e => setDosis(e.target.value)} placeholder="Ej: 500mg, 1g, 10ml" /></div>
<div className="grid grid-cols-2 gap-4">
<div><Label>Frecuencia (hs)</Label><Input type="number" value={frecuenciaHoras} onChange={e => setFrecuenciaHoras(e.target.value ? parseInt(e.target.value) : '')} placeholder="Cada X horas" /></div>
<div><Label>Vía</Label><Select value={via} onValueChange={(v: ViaAdministracion) => setVia(v)}><SelectTrigger><SelectValue /></SelectTrigger><SelectContent>{vias.map(v => <SelectItem key={v} value={v}>{v}</SelectItem>)}</SelectContent></Select></div>
</div>
</>
)}
{tipo === 'Farmacologica Profilactica' && (
<> <>
<div><Label>Droga *</Label><Input value={droga} onChange={e => setDroga(e.target.value)} placeholder="Nombre del medicamento" /></div> <div><Label>Droga *</Label><Input value={droga} onChange={e => setDroga(e.target.value)} placeholder="Nombre del medicamento" /></div>
<div><Label>Dosis *</Label><Input value={dosis} onChange={e => setDosis(e.target.value)} placeholder="Ej: 500mg, 1g, 10ml" /></div> <div><Label>Dosis *</Label><Input value={dosis} onChange={e => setDosis(e.target.value)} placeholder="Ej: 500mg, 1g, 10ml" /></div>
@@ -194,7 +272,6 @@ export function SeccionIndicaciones({ recomendaciones, internacionId, add, updat
</div> </div>
</> </>
)} )}
{tipo === 'Farmacologica Insulina' && ( {tipo === 'Farmacologica Insulina' && (
<> <>
<div><Label>Tipo de Insulina</Label><Select value={tipoInsulina} onValueChange={(v: TipoInsulina) => setTipoInsulina(v)}><SelectTrigger><SelectValue /></SelectTrigger><SelectContent>{tiposInsulina.map(t => <SelectItem key={t} value={t}>{t}</SelectItem>)}</SelectContent></Select></div> <div><Label>Tipo de Insulina</Label><Select value={tipoInsulina} onValueChange={(v: TipoInsulina) => setTipoInsulina(v)}><SelectTrigger><SelectValue /></SelectTrigger><SelectContent>{tiposInsulina.map(t => <SelectItem key={t} value={t}>{t}</SelectItem>)}</SelectContent></Select></div>
@@ -210,7 +287,7 @@ export function SeccionIndicaciones({ recomendaciones, internacionId, add, updat
<div><Label>Descripción *</Label><textarea className="w-full p-2 border rounded-md text-sm min-h-[80px] bg-transparent dark:bg-input/30" value={descripcion} onChange={e => setDescripcion(e.target.value)} placeholder="Ej: Control de signos vitales, Dieta, etc." /></div> <div><Label>Descripción *</Label><textarea className="w-full p-2 border rounded-md text-sm min-h-[80px] bg-transparent dark:bg-input/30" value={descripcion} onChange={e => setDescripcion(e.target.value)} placeholder="Ej: Control de signos vitales, Dieta, etc." /></div>
)} )}
{(tipo === 'PHP' || tipo === 'Paralelo') && ( {(tipo === 'PHP' || tipo === 'PHP Paralelo') && (
<> <>
<div><Label>Tipo de Plan</Label><Select value={tipoPlan} onValueChange={(v: TipoPlanHidratacion) => setTipoPlan(v)}><SelectTrigger><SelectValue /></SelectTrigger><SelectContent>{planes.map(p => <SelectItem key={p} value={p}>{p}</SelectItem>)}</SelectContent></Select></div> <div><Label>Tipo de Plan</Label><Select value={tipoPlan} onValueChange={(v: TipoPlanHidratacion) => setTipoPlan(v)}><SelectTrigger><SelectValue /></SelectTrigger><SelectContent>{planes.map(p => <SelectItem key={p} value={p}>{p}</SelectItem>)}</SelectContent></Select></div>
<div className="grid grid-cols-2 gap-4"> <div className="grid grid-cols-2 gap-4">
@@ -220,7 +297,7 @@ export function SeccionIndicaciones({ recomendaciones, internacionId, add, updat
</> </>
)} )}
{tipo === 'Alterno' && ( {tipo === 'PHP Alterno' && (
<> <>
<div><Label>Plan 1</Label><Select value={tipoPlan} onValueChange={(v: TipoPlanHidratacion) => setTipoPlan(v)}><SelectTrigger><SelectValue /></SelectTrigger><SelectContent>{planes.map(p => <SelectItem key={p} value={p}>{p}</SelectItem>)}</SelectContent></Select></div> <div><Label>Plan 1</Label><Select value={tipoPlan} onValueChange={(v: TipoPlanHidratacion) => setTipoPlan(v)}><SelectTrigger><SelectValue /></SelectTrigger><SelectContent>{planes.map(p => <SelectItem key={p} value={p}>{p}</SelectItem>)}</SelectContent></Select></div>
<div className="grid grid-cols-2 gap-4"> <div className="grid grid-cols-2 gap-4">
@@ -256,6 +333,9 @@ export function SeccionIndicaciones({ recomendaciones, internacionId, add, updat
{ind.tipo === 'Farmacologica' && ( {ind.tipo === 'Farmacologica' && (
<div className="font-medium">{ind.droga} {ind.dosis} {ind.frecuenciaHoras ? `c/${ind.frecuenciaHoras}hs` : ''} {ind.via}</div> <div className="font-medium">{ind.droga} {ind.dosis} {ind.frecuenciaHoras ? `c/${ind.frecuenciaHoras}hs` : ''} {ind.via}</div>
)} )}
{ind.tipo === 'Farmacologica Profilactica' && (
<div className="font-medium">{ind.droga} {ind.dosis} {ind.frecuenciaHoras ? `c/${ind.frecuenciaHoras}hs` : ''} {ind.via}</div>
)}
{ind.tipo === 'Farmacologica Insulina' && ( {ind.tipo === 'Farmacologica Insulina' && (
<div className="font-medium">{ind.tipoInsulina} <div className="font-medium">{ind.tipoInsulina}
{ind.unidadesDesayuno && ` - Desayuno: ${ind.unidadesDesayuno}U`} {ind.unidadesDesayuno && ` - Desayuno: ${ind.unidadesDesayuno}U`}
@@ -266,13 +346,10 @@ export function SeccionIndicaciones({ recomendaciones, internacionId, add, updat
{ind.tipo === 'No Farmacologica' && ( {ind.tipo === 'No Farmacologica' && (
<div className="font-medium">{ind.descripcion}</div> <div className="font-medium">{ind.descripcion}</div>
)} )}
{(ind.tipo === 'PHP' || ind.tipo === 'Paralelo') && ( {(ind.tipo === 'PHP' || ind.tipo === 'PHP Paralelo') && (
<div className="font-medium">{ind.tipoPlan} {ind.cantidadMl}ml en {ind.tiempoHoras}hs</div> <div className="font-medium">{ind.tipoPlan} {ind.cantidadMl}ml en {ind.tiempoHoras}hs</div>
)} )}
{ind.tipo === 'Alterno' && ( {ind.tipo === 'PHP Alterno' && (
<div className="font-medium">{ind.tipoPlan} {ind.cantidadMl}ml + {ind.tipoPlan2} {ind.cantidadMl2}ml en {ind.tiempoHoras}hs</div>
)}
{ind.tipo === 'Alterno' && (
<div className="font-medium">{ind.tipoPlan} {ind.cantidadMl}ml + {ind.tipoPlan2} {ind.cantidadMl2}ml en {ind.tiempoHoras}hs</div> <div className="font-medium">{ind.tipoPlan} {ind.cantidadMl}ml + {ind.tipoPlan2} {ind.cantidadMl2}ml en {ind.tiempoHoras}hs</div>
)} )}
</div> </div>
@@ -302,42 +379,55 @@ export function SeccionIndicaciones({ recomendaciones, internacionId, add, updat
</div> </div>
)} )}
{showHistorial && historial.length > 0 && ( {showHistorial && (
<div className="mt-6"> <Dialog open={showHistorial} onOpenChange={setShowHistorial}>
<h3 className="font-bold mb-2">Historial de Indicaciones</h3> <DialogContent className="max-w-4xl max-h-[80vh] overflow-auto">
<DialogHeader>
<DialogTitle>Historial de Indicaciones</DialogTitle>
</DialogHeader>
{movs.length === 0 ? (
<p className="text-gray-500 py-4">Sin movimientos registrados</p>
) : (
<Table> <Table>
<TableHeader> <TableHeader>
<TableRow> <TableRow>
<TableHead>Fecha Creación</TableHead> <TableHead className="cursor-pointer hover:bg-gray-100" onClick={() => handleSort('fecha')}>
<TableHead>Creado por</TableHead> Fecha {sortField === 'fecha' && (sortDir === 'asc' ? '↑' : '↓')}
<TableHead>Última Modificación</TableHead> </TableHead>
<TableHead>Modificado por</TableHead> <TableHead className="cursor-pointer hover:bg-gray-100" onClick={() => handleSort('profesional')}>
<TableHead>Fecha Suspensión</TableHead> Profesional {sortField === 'profesional' && (sortDir === 'asc' ? '↑' : '↓')}
<TableHead>Suspendido por</TableHead> </TableHead>
<TableHead className="cursor-pointer hover:bg-gray-100" onClick={() => handleSort('tipo')}>
Tipo {sortField === 'tipo' && (sortDir === 'asc' ? '↑' : '↓')}
</TableHead>
<TableHead>Indicación</TableHead> <TableHead>Indicación</TableHead>
</TableRow> </TableRow>
</TableHeader> </TableHeader>
<TableBody> <TableBody>
{historial.map(ind => ( {sortedMovs.map(mov => (
<TableRow key={ind.id}> <TableRow key={mov.id}>
<TableCell>{ind.fechaCrea}</TableCell> <TableCell>{mov.fecha}</TableCell>
<TableCell>{ind.medicoCrea}</TableCell> <TableCell>{mov.profesional}</TableCell>
<TableCell>{ind.fechaModificacion || '-'}</TableCell>
<TableCell>{ind.medicoModifica || '-'}</TableCell>
<TableCell>{ind.fechaSuspension || '-'}</TableCell>
<TableCell>{ind.medicoSuspende || '-'}</TableCell>
<TableCell> <TableCell>
{ind.tipo === 'Farmacologica' && `${ind.droga} ${ind.dosis} ${ind.frecuenciaHoras ? `c/${ind.frecuenciaHoras}hs` : ''} ${ind.via}`} <Badge className={mov.tipo === 'Indicacion' ? 'bg-green-500' : mov.tipo === 'Suspencion' ? 'bg-red-500' : 'bg-orange-500'}>
{ind.tipo === 'Farmacologica Insulina' && `${ind.tipoInsulina}${ind.unidadesDesayuno ? ` - Desayuno: ${ind.unidadesDesayuno}U` : ''}${ind.unidadesAlmuerzo ? ` - Almuerzo: ${ind.unidadesAlmuerzo}U` : ''}${ind.unidadesNoche ? ` - 23hs: ${ind.unidadesNoche}U` : ''}`} {mov.tipo === 'Indicacion' ? 'Indicación' : mov.tipo === 'Suspencion' ? 'Suspensión' : 'Modificación'}
{ind.tipo === 'No Farmacologica' && ind.descripcion} </Badge>
{(ind.tipo === 'PHP' || ind.tipo === 'Paralelo') && `${ind.tipoPlan} ${ind.cantidadMl}ml en ${ind.tiempoHoras}hs`} </TableCell>
{ind.tipo === 'Alterno' && `${ind.tipoPlan} ${ind.cantidadMl}ml + ${ind.tipoPlan2} ${ind.cantidadMl2}ml en ${ind.tiempoHoras}hs`} <TableCell>
{mov.tipo === 'Modificacion' ? (
<div className="text-sm">
<div className="text-red-500 line-through">{mov.indicacionPrevia}</div>
<div className="text-green-500">{mov.indicacionNueva}</div>
</div>
) : mov.tipo === 'Suspencion' ? mov.indicacionPrevia : mov.indicacionNueva}
</TableCell> </TableCell>
</TableRow> </TableRow>
))} ))}
</TableBody> </TableBody>
</Table> </Table>
</div> )}
</DialogContent>
</Dialog>
)} )}
</div> </div>
); );
+2 -2
View File
@@ -172,9 +172,9 @@ export interface ATB {
fechaFinalizacion?: string; fechaFinalizacion?: string;
} }
export type IndicacionTipo = 'Farmacologica' | 'Farmacologica Insulina' | 'No Farmacologica' | 'PHP' | 'Paralelo' | 'Alterno'; export type IndicacionTipo = 'Farmacologica' | 'Farmacologica Profilactica' | 'Farmacologica Insulina' | 'No Farmacologica' | 'PHP' | 'PHP Paralelo' | 'PHP Alterno';
export type TipoInsulina = 'NPH' | 'Glargina'; export type TipoInsulina = 'NPH' | 'Glargina';
export type ViaAdministracion = 'Oral' | 'EV' | 'IM' | 'SC' | 'Por GGT' | 'Por SNG'; export type ViaAdministracion = 'Via Oral' | 'EV' | 'IM' | 'SC' | 'Por GGT' | 'Por SNG';
export type TipoPlanHidratacion = 'SF 0.9%' | 'Dextrosa 5%' | 'Dextrosa 10%' | 'Dextrosa 25%' | 'Ringer Lactato'; export type TipoPlanHidratacion = 'SF 0.9%' | 'Dextrosa 5%' | 'Dextrosa 10%' | 'Dextrosa 25%' | 'Ringer Lactato';
export interface Indicacion { export interface Indicacion {