Actualizacion de sistema: mejoras en seleccion de categorias de cultivos, optimizacion responsive de listado de cultivos e historia clinica, y correccion de componentes
This commit is contained in:
@@ -12,6 +12,7 @@ import { initDb,
|
||||
getAllGlucemias, createGlucemia, updateGlucemia, deleteGlucemia,
|
||||
getAllAcidosBase, createAcidoBase, updateAcidoBase, deleteAcidoBase,
|
||||
getAllCultivos, createCultivo, updateCultivo, deleteCultivo,
|
||||
getAllTiposCultivo, createTipoCultivo, updateTipoCultivo, deleteTipoCultivo, restablecerTiposCultivo,
|
||||
getAllEstudiosComplementarios, createEstudioComplementario, updateEstudioComplementario, deleteEstudioComplementario,
|
||||
getAllInterconsultas, createInterconsulta, updateInterconsulta, deleteInterconsulta,
|
||||
getAllAtb, createAtb, updateAtb, deleteAtb,
|
||||
@@ -95,6 +96,7 @@ app.get('/api/state', async (req, res) => {
|
||||
glucemias: await getAllGlucemias(),
|
||||
acidosBase: await getAllAcidosBase(),
|
||||
cultivos: await getAllCultivos(),
|
||||
tiposCultivo: await getAllTiposCultivo(),
|
||||
estudiosComplementarios: await getAllEstudiosComplementarios(),
|
||||
interconsultas: (await getAllInterconsultas()).map(ic => ({ ...ic, realizada: !!ic.realizada })),
|
||||
atb: await getAllAtb(),
|
||||
@@ -580,6 +582,51 @@ app.delete('/api/cultivos/:id', async (req, res) => {
|
||||
}
|
||||
});
|
||||
|
||||
// ========== TIPOS DE CULTIVO ==========
|
||||
app.get('/api/tipos-cultivo', async (req, res) => {
|
||||
try {
|
||||
res.json(await getAllTiposCultivo());
|
||||
} catch (err) {
|
||||
res.status(500).json({ error: 'Error al obtener tipos de cultivo' });
|
||||
}
|
||||
});
|
||||
|
||||
app.post('/api/tipos-cultivo', async (req, res) => {
|
||||
try {
|
||||
const nuevo = await createTipoCultivo(req.body);
|
||||
res.json(nuevo);
|
||||
} catch (err) {
|
||||
res.status(400).json({ error: err.message || 'Error al crear tipo de cultivo' });
|
||||
}
|
||||
});
|
||||
|
||||
app.put('/api/tipos-cultivo/:id', async (req, res) => {
|
||||
try {
|
||||
const updated = await updateTipoCultivo(req.params.id, req.body);
|
||||
res.json(updated);
|
||||
} catch (err) {
|
||||
res.status(400).json({ error: err.message || 'Error al actualizar tipo de cultivo' });
|
||||
}
|
||||
});
|
||||
|
||||
app.delete('/api/tipos-cultivo/:id', async (req, res) => {
|
||||
try {
|
||||
await deleteTipoCultivo(req.params.id);
|
||||
res.json({ ok: true });
|
||||
} catch (err) {
|
||||
res.status(500).json({ error: 'Error al eliminar tipo de cultivo' });
|
||||
}
|
||||
});
|
||||
|
||||
app.post('/api/tipos-cultivo/reset', async (req, res) => {
|
||||
try {
|
||||
const list = await restablecerTiposCultivo();
|
||||
res.json(list);
|
||||
} catch (err) {
|
||||
res.status(500).json({ error: 'Error al restablecer tipos de cultivo' });
|
||||
}
|
||||
});
|
||||
|
||||
// ========== ESTUDIOS COMPLEMENTARIOS ==========
|
||||
app.get('/api/estudios-complementarios', async (req, res) => {
|
||||
try {
|
||||
|
||||
+158
-2
@@ -66,6 +66,8 @@ const memStore = {
|
||||
indicaciones: [],
|
||||
movimientos_indicaciones: [],
|
||||
pendientes: [],
|
||||
tipos_cultivo: [],
|
||||
otrosLaboratorios: [],
|
||||
kv: {}
|
||||
};
|
||||
|
||||
@@ -703,6 +705,160 @@ export async function deleteCultivo(id) {
|
||||
}
|
||||
}
|
||||
|
||||
// ========== TIPOS DE CULTIVO ==========
|
||||
export const DEFAULT_TIPOS_CULTIVO = [
|
||||
{ id: '1', nombre: 'HMCx2', categoria: 'Hemocultivos', descripcion: 'Hemocultivos seriados x2' },
|
||||
{ id: '2', nombre: 'RC', categoria: 'Catéter', descripcion: 'Retro cultivo / punta de catéter' },
|
||||
{ id: '3', nombre: 'PC', categoria: 'Punción', descripcion: 'Punción cultivo' },
|
||||
{ id: '4', nombre: 'UC', categoria: 'Urocultivo', descripcion: 'Urocultivo / muestra de orina' },
|
||||
{ id: '5', nombre: 'LCR', categoria: 'Líquidos', descripcion: 'Líquido cefalorraquídeo' },
|
||||
{ id: '6', nombre: 'LP', categoria: 'Líquidos', descripcion: 'Líquido pleural' },
|
||||
{ id: '7', nombre: 'LAsc', categoria: 'Líquidos', descripcion: 'Líquido ascítico' },
|
||||
{ id: '8', nombre: 'LAbd', categoria: 'Líquidos', descripcion: 'Líquido abdominal' },
|
||||
{ id: '9', nombre: 'Coleccion', categoria: 'Líquidos y Colecciones', descripcion: 'Muestra de colección / absceso' },
|
||||
{ id: '10', nombre: 'Partes Blandas', categoria: 'Tejidos', descripcion: 'Cultivo de partes blandas / tejido' },
|
||||
{ id: '11', nombre: 'Esputo GC', categoria: 'Respiratorio', descripcion: 'Esputo Germen Común' },
|
||||
{ id: '12', nombre: 'Esputo TBC', categoria: 'Respiratorio', descripcion: 'Esputo Tuberculosis' },
|
||||
{ id: '13', nombre: 'Baciloscopia', categoria: 'Respiratorio', descripcion: 'Baciloscopia directa' },
|
||||
{ id: '14', nombre: 'HNF Test Rápido', categoria: 'Hisopado Nasofaríngeo', descripcion: 'Hisopado nasofaríngeo test rápido' },
|
||||
{ id: '15', nombre: 'HNF Panel PCR', categoria: 'Hisopado Nasofaríngeo', descripcion: 'Hisopado nasofaríngeo panel PCR virológico' },
|
||||
{ id: '16', nombre: 'Hisopado Rectal KPC', categoria: 'Vigilancia Epidemiológica', descripcion: 'Hisopado rectal para screening de KPC/BLEE' }
|
||||
];
|
||||
|
||||
export async function getAllTiposCultivo() {
|
||||
let list = [];
|
||||
if (db) {
|
||||
const raw = await db.collection('tipos_cultivo').find().toArray();
|
||||
list = cleanDocs(raw);
|
||||
if (list.length === 0) {
|
||||
// Seed default
|
||||
for (const item of DEFAULT_TIPOS_CULTIVO) {
|
||||
await db.collection('tipos_cultivo').insertOne({ ...item });
|
||||
}
|
||||
list = [...DEFAULT_TIPOS_CULTIVO];
|
||||
}
|
||||
// Also read any existing custom tipoMuestra from registered cultivos that might not be in the list
|
||||
const allCultivos = await db.collection('cultivos').find().toArray();
|
||||
const existingNombres = new Set(list.map(t => (t.nombre || '').trim().toLowerCase()));
|
||||
for (const c of allCultivos) {
|
||||
if (c.tipoMuestra && c.tipoMuestra.trim() && !existingNombres.has(c.tipoMuestra.trim().toLowerCase())) {
|
||||
const nuevoTipo = {
|
||||
id: generateUUID(),
|
||||
nombre: c.tipoMuestra.trim(),
|
||||
categoria: 'Personalizado',
|
||||
descripcion: 'Importado automáticamente desde registro existente de cultivo'
|
||||
};
|
||||
await db.collection('tipos_cultivo').insertOne(nuevoTipo);
|
||||
list.push(nuevoTipo);
|
||||
existingNombres.add(c.tipoMuestra.trim().toLowerCase());
|
||||
}
|
||||
}
|
||||
return list;
|
||||
} else {
|
||||
if (!memStore.tipos_cultivo || memStore.tipos_cultivo.length === 0) {
|
||||
memStore.tipos_cultivo = DEFAULT_TIPOS_CULTIVO.map(item => ({ ...item }));
|
||||
}
|
||||
list = [...memStore.tipos_cultivo];
|
||||
const existingNombres = new Set(list.map(t => (t.nombre || '').trim().toLowerCase()));
|
||||
for (const c of (memStore.cultivos || [])) {
|
||||
if (c.tipoMuestra && c.tipoMuestra.trim() && !existingNombres.has(c.tipoMuestra.trim().toLowerCase())) {
|
||||
const nuevoTipo = {
|
||||
id: generateUUID(),
|
||||
nombre: c.tipoMuestra.trim(),
|
||||
categoria: 'Personalizado',
|
||||
descripcion: 'Importado automáticamente desde registro existente de cultivo'
|
||||
};
|
||||
memStore.tipos_cultivo.push(nuevoTipo);
|
||||
list.push(nuevoTipo);
|
||||
existingNombres.add(c.tipoMuestra.trim().toLowerCase());
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
}
|
||||
|
||||
export async function createTipoCultivo(tipo) {
|
||||
const doc = {
|
||||
id: tipo.id || generateUUID(),
|
||||
nombre: (tipo.nombre || '').trim(),
|
||||
categoria: (tipo.categoria || 'General').trim(),
|
||||
descripcion: (tipo.descripcion || '').trim()
|
||||
};
|
||||
if (!doc.nombre) {
|
||||
throw new Error('El nombre del tipo de cultivo es requerido');
|
||||
}
|
||||
|
||||
if (db) {
|
||||
const existing = await db.collection('tipos_cultivo').findOne({ nombre: doc.nombre });
|
||||
if (existing) {
|
||||
throw new Error(`Ya existe un tipo de cultivo con el nombre "${doc.nombre}"`);
|
||||
}
|
||||
await db.collection('tipos_cultivo').insertOne(doc);
|
||||
} else {
|
||||
if (!memStore.tipos_cultivo) memStore.tipos_cultivo = [];
|
||||
const exists = memStore.tipos_cultivo.some(t => t.nombre.toLowerCase() === doc.nombre.toLowerCase());
|
||||
if (exists) {
|
||||
throw new Error(`Ya existe un tipo de cultivo con el nombre "${doc.nombre}"`);
|
||||
}
|
||||
memStore.tipos_cultivo.push(doc);
|
||||
}
|
||||
return doc;
|
||||
}
|
||||
|
||||
export async function updateTipoCultivo(id, datos) {
|
||||
const updateDoc = {};
|
||||
if (datos.nombre !== undefined) updateDoc.nombre = datos.nombre.trim();
|
||||
if (datos.categoria !== undefined) updateDoc.categoria = datos.categoria.trim();
|
||||
if (datos.descripcion !== undefined) updateDoc.descripcion = datos.descripcion.trim();
|
||||
|
||||
if (db) {
|
||||
if (updateDoc.nombre) {
|
||||
const existing = await db.collection('tipos_cultivo').findOne({ nombre: updateDoc.nombre, id: { $ne: id } });
|
||||
if (existing) {
|
||||
throw new Error(`Ya existe otro tipo de cultivo con el nombre "${updateDoc.nombre}"`);
|
||||
}
|
||||
}
|
||||
await db.collection('tipos_cultivo').updateOne({ id }, { $set: updateDoc });
|
||||
} else {
|
||||
if (!memStore.tipos_cultivo) memStore.tipos_cultivo = [];
|
||||
if (updateDoc.nombre) {
|
||||
const exists = memStore.tipos_cultivo.some(t => t.id !== id && t.nombre.toLowerCase() === updateDoc.nombre.toLowerCase());
|
||||
if (exists) {
|
||||
throw new Error(`Ya existe otro tipo de cultivo con el nombre "${updateDoc.nombre}"`);
|
||||
}
|
||||
}
|
||||
const idx = memStore.tipos_cultivo.findIndex(t => t.id === id);
|
||||
if (idx !== -1) {
|
||||
memStore.tipos_cultivo[idx] = { ...memStore.tipos_cultivo[idx], ...updateDoc };
|
||||
}
|
||||
}
|
||||
return { id, ...datos };
|
||||
}
|
||||
|
||||
export async function deleteTipoCultivo(id) {
|
||||
if (db) {
|
||||
await db.collection('tipos_cultivo').deleteOne({ id });
|
||||
} else {
|
||||
if (memStore.tipos_cultivo) {
|
||||
memStore.tipos_cultivo = memStore.tipos_cultivo.filter(t => t.id !== id);
|
||||
}
|
||||
}
|
||||
return { success: true };
|
||||
}
|
||||
|
||||
export async function restablecerTiposCultivo() {
|
||||
if (db) {
|
||||
await db.collection('tipos_cultivo').deleteMany({});
|
||||
for (const item of DEFAULT_TIPOS_CULTIVO) {
|
||||
await db.collection('tipos_cultivo').insertOne({ ...item });
|
||||
}
|
||||
return await getAllTiposCultivo();
|
||||
} else {
|
||||
memStore.tipos_cultivo = DEFAULT_TIPOS_CULTIVO.map(item => ({ ...item }));
|
||||
return [...memStore.tipos_cultivo];
|
||||
}
|
||||
}
|
||||
|
||||
// ========== ESTUDIOS COMPLEMENTARIOS ==========
|
||||
export async function getAllEstudiosComplementarios() {
|
||||
if (db) {
|
||||
@@ -1003,7 +1159,7 @@ export async function exportAllData() {
|
||||
const collections = [
|
||||
'usuarios', 'pacientes', 'areas', 'camas', 'internaciones',
|
||||
'evoluciones', 'laboratorios', 'glucemias', 'acidosbase',
|
||||
'cultivos', 'estudiosComplementarios', 'interconsultas', 'atb',
|
||||
'cultivos', 'tipos_cultivo', 'estudiosComplementarios', 'interconsultas', 'atb',
|
||||
'indicaciones', 'movimientos_indicaciones', 'pendientes', 'kv'
|
||||
];
|
||||
const dump = {};
|
||||
@@ -1024,7 +1180,7 @@ export async function importAllData(dump) {
|
||||
const collections = [
|
||||
'usuarios', 'pacientes', 'areas', 'camas', 'internaciones',
|
||||
'evoluciones', 'laboratorios', 'glucemias', 'acidosbase',
|
||||
'cultivos', 'estudiosComplementarios', 'interconsultas', 'atb',
|
||||
'cultivos', 'tipos_cultivo', 'estudiosComplementarios', 'interconsultas', 'atb',
|
||||
'indicaciones', 'movimientos_indicaciones', 'pendientes', 'kv'
|
||||
];
|
||||
if (db) {
|
||||
|
||||
Reference in New Issue
Block a user