fix(HistoriaClinica): agregar handlers de otrosLaboratorios en store y corregir ReferenceError
This commit is contained in:
@@ -0,0 +1,29 @@
|
|||||||
|
const fs = require('fs');
|
||||||
|
let code = fs.readFileSync('src/sections/HistoriaClinica.tsx', 'utf8');
|
||||||
|
|
||||||
|
const regex = /<div className="flex justify-between">\s*<div className="flex gap-2">\s*<DropdownMenu>[\s\S]*?<Button variant="outline" onClick=\{\(\) => setOtrosDialog\(true\)\}><Eye className="h-4 w-4 mr-2" \/>Otros<\/Button>\s*<Button variant="outline" onClick=\{\(\) => \{ setImportFecha\(getLocalToday\(\)\); setImportHora\(new Date\(\)\.toTimeString\(\)\.slice\(0, 5\)\); setImportTexto\(''\); setImportResultados\(\[\]\); setImportObservaciones\(''\); setImportDialog\(true\); \}\}>\s*<FileText className="h-4 w-4 mr-2" \/>Importar\s*<\/Button>\s*<\/div>\s*<\/div>/g;
|
||||||
|
|
||||||
|
const match = code.match(regex);
|
||||||
|
if (match) {
|
||||||
|
const content = match[0].replace('<div className="flex justify-between">\n <div className="flex gap-2">', '').replace('</div>\n </div>', '').trim();
|
||||||
|
|
||||||
|
const repl = `
|
||||||
|
{portalNode ? createPortal(
|
||||||
|
<>
|
||||||
|
${content}
|
||||||
|
</>,
|
||||||
|
portalNode
|
||||||
|
) : (
|
||||||
|
<div className="flex justify-between mb-4">
|
||||||
|
<div className="flex gap-2">
|
||||||
|
${content}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
`.trim();
|
||||||
|
|
||||||
|
code = code.replace(regex, repl);
|
||||||
|
fs.writeFileSync('src/sections/HistoriaClinica.tsx', code);
|
||||||
|
} else {
|
||||||
|
console.log("NOT FOUND");
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
const fs = require('fs');
|
||||||
|
let code = fs.readFileSync('src/sections/HistoriaClinica.tsx', 'utf8');
|
||||||
|
|
||||||
|
const regex = /function SeccionLaboratorios\(\{ lab, patientId, internacionId, add, update, del, addAcidoBase, canEdit, portalNode\}: \{([\s\S]*?)\}\) \{/;
|
||||||
|
|
||||||
|
const repl = `function SeccionLaboratorios({ lab, otrosLaboratorios = [], patientId, internacionId, add, update, del, addAcidoBase, addOtroLaboratorio, canEdit, portalNode}: {
|
||||||
|
lab: Laboratorio[];
|
||||||
|
otrosLaboratorios?: OtroLaboratorio[];
|
||||||
|
patientId: string;
|
||||||
|
internacionId: string;
|
||||||
|
add: (l: Omit<Laboratorio, 'id'>) => void;
|
||||||
|
update: (id: string, data: Partial<Laboratorio>) => void;
|
||||||
|
del: (id: string) => void;
|
||||||
|
addAcidoBase?: (a: Omit<AcidoBase, 'id'>) => void;
|
||||||
|
addOtroLaboratorio?: (o: Omit<OtroLaboratorio, 'id'>) => void;
|
||||||
|
canEdit?: boolean;
|
||||||
|
portalNode?: HTMLDivElement | null;
|
||||||
|
}) {`;
|
||||||
|
|
||||||
|
code = code.replace(regex, repl);
|
||||||
|
fs.writeFileSync('src/sections/HistoriaClinica.tsx', code);
|
||||||
+143
@@ -0,0 +1,143 @@
|
|||||||
|
const fs = require('fs');
|
||||||
|
|
||||||
|
// 1. UPDATE db-mongodb.js
|
||||||
|
let code = fs.readFileSync('server/db-mongodb.js', 'utf8');
|
||||||
|
|
||||||
|
// add memStore
|
||||||
|
code = code.replace("laboratorios: [],", "laboratorios: [],\n otrosLaboratorios: [],");
|
||||||
|
|
||||||
|
// add methods
|
||||||
|
const addMethods = `
|
||||||
|
export async function getAllOtrosLaboratorios() {
|
||||||
|
if (db) {
|
||||||
|
const records = await db.collection('otros-laboratorios').find().toArray();
|
||||||
|
return cleanDocs(records);
|
||||||
|
}
|
||||||
|
return [...memStore.otrosLaboratorios];
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function createOtroLaboratorio(record) {
|
||||||
|
const doc = {
|
||||||
|
id: record.id,
|
||||||
|
pacienteId: record.pacienteId,
|
||||||
|
internacionId: record.internacionId,
|
||||||
|
fecha: record.fecha,
|
||||||
|
hora: record.hora,
|
||||||
|
observaciones: record.observaciones,
|
||||||
|
createdAt: new Date().toISOString()
|
||||||
|
};
|
||||||
|
|
||||||
|
if (db) {
|
||||||
|
await db.collection('otros-laboratorios').insertOne(doc);
|
||||||
|
return cleanDoc(doc);
|
||||||
|
}
|
||||||
|
|
||||||
|
memStore.otrosLaboratorios.push(doc);
|
||||||
|
return doc;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function updateOtroLaboratorio(id, updates) {
|
||||||
|
if (db) {
|
||||||
|
await db.collection('otros-laboratorios').updateOne(
|
||||||
|
{ id },
|
||||||
|
{ $set: { ...updates, updatedAt: new Date().toISOString() } }
|
||||||
|
);
|
||||||
|
return { success: true };
|
||||||
|
}
|
||||||
|
|
||||||
|
const idx = memStore.otrosLaboratorios.findIndex(x => x.id === id);
|
||||||
|
if (idx !== -1) {
|
||||||
|
memStore.otrosLaboratorios[idx] = { ...memStore.otrosLaboratorios[idx], ...updates, updatedAt: new Date().toISOString() };
|
||||||
|
return { success: true };
|
||||||
|
}
|
||||||
|
return { success: false };
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function deleteOtroLaboratorio(id) {
|
||||||
|
if (db) {
|
||||||
|
await db.collection('otros-laboratorios').deleteOne({ id });
|
||||||
|
return { success: true };
|
||||||
|
}
|
||||||
|
|
||||||
|
const idx = memStore.otrosLaboratorios.findIndex(x => x.id === id);
|
||||||
|
if (idx !== -1) {
|
||||||
|
memStore.otrosLaboratorios.splice(idx, 1);
|
||||||
|
return { success: true };
|
||||||
|
}
|
||||||
|
return { success: false };
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
code = code + "\n" + addMethods;
|
||||||
|
|
||||||
|
// exportAllData & importAllData
|
||||||
|
code = code.replace("laboratorios: await getAllLaboratorios(),", "laboratorios: await getAllLaboratorios(),\n otrosLaboratorios: await getAllOtrosLaboratorios(),");
|
||||||
|
code = code.replace("if (data.laboratorios) memStore.laboratorios = data.laboratorios;", "if (data.laboratorios) memStore.laboratorios = data.laboratorios;\n if (data.otrosLaboratorios) memStore.otrosLaboratorios = data.otrosLaboratorios;");
|
||||||
|
|
||||||
|
fs.writeFileSync('server/db-mongodb.js', code);
|
||||||
|
|
||||||
|
|
||||||
|
// 2. UPDATE api-mongodb.js
|
||||||
|
let apiCode = fs.readFileSync('server/api-mongodb.js', 'utf8');
|
||||||
|
|
||||||
|
apiCode = apiCode.replace("getAllLaboratorios, createLaboratorio, updateLaboratorio, deleteLaboratorio,", "getAllLaboratorios, createLaboratorio, updateLaboratorio, deleteLaboratorio,\n getAllOtrosLaboratorios, createOtroLaboratorio, updateOtroLaboratorio, deleteOtroLaboratorio,");
|
||||||
|
|
||||||
|
apiCode = apiCode.replace("laboratorios: (await getAllLaboratorios()).map(l => ({ ...l, resultados: typeof l.resultados === 'string' ? JSON.parse(l.resultados) : l.resultados })),", "laboratorios: (await getAllLaboratorios()).map(l => ({ ...l, resultados: typeof l.resultados === 'string' ? JSON.parse(l.resultados) : l.resultados })),\n otrosLaboratorios: await getAllOtrosLaboratorios(),");
|
||||||
|
|
||||||
|
const apiMethods = `
|
||||||
|
// ========== OTROS LABORATORIOS ==========
|
||||||
|
app.get('/api/otros-laboratorios', async (req, res) => {
|
||||||
|
try {
|
||||||
|
const records = await getAllOtrosLaboratorios();
|
||||||
|
res.json(records);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error fetching otros-laboratorios:', error);
|
||||||
|
res.status(500).json({ error: 'Failed to fetch otros-laboratorios' });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
app.post('/api/otros-laboratorios', async (req, res) => {
|
||||||
|
try {
|
||||||
|
const { pacienteId, fecha, observaciones } = req.body;
|
||||||
|
if (!pacienteId || !fecha || !observaciones) {
|
||||||
|
return res.status(400).json({ error: 'pacienteId, fecha and observaciones are required' });
|
||||||
|
}
|
||||||
|
|
||||||
|
const record = {
|
||||||
|
id: generateUUID(),
|
||||||
|
...req.body
|
||||||
|
};
|
||||||
|
|
||||||
|
const newRecord = await createOtroLaboratorio(record);
|
||||||
|
res.status(201).json(newRecord);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error creating otro laboratorio:', error);
|
||||||
|
res.status(500).json({ error: 'Failed to create otro laboratorio' });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
app.put('/api/otros-laboratorios/:id', async (req, res) => {
|
||||||
|
try {
|
||||||
|
const result = await updateOtroLaboratorio(req.params.id, req.body);
|
||||||
|
if (!result.success) return res.status(404).json({ error: 'Record not found' });
|
||||||
|
res.json(result);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error updating otro laboratorio:', error);
|
||||||
|
res.status(500).json({ error: 'Failed to update otro laboratorio' });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
app.delete('/api/otros-laboratorios/:id', async (req, res) => {
|
||||||
|
try {
|
||||||
|
const result = await deleteOtroLaboratorio(req.params.id);
|
||||||
|
if (!result.success) return res.status(404).json({ error: 'Record not found' });
|
||||||
|
res.json({ message: 'Deleted successfully' });
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error deleting otro laboratorio:', error);
|
||||||
|
res.status(500).json({ error: 'Failed to delete otro laboratorio' });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
`;
|
||||||
|
|
||||||
|
apiCode = apiCode.replace("// ========== GLUCEMIAS ==========", apiMethods + "\n// ========== GLUCEMIAS ==========");
|
||||||
|
fs.writeFileSync('server/api-mongodb.js', apiCode);
|
||||||
@@ -0,0 +1,88 @@
|
|||||||
|
const fs = require('fs');
|
||||||
|
let code = fs.readFileSync('src/hooks/useHospitalStore.ts', 'utf8');
|
||||||
|
|
||||||
|
// add import
|
||||||
|
code = code.replace("Laboratorio, ", "Laboratorio, OtroLaboratorio, ");
|
||||||
|
|
||||||
|
// add state array
|
||||||
|
code = code.replace("laboratorios: Laboratorio[];", "laboratorios: Laboratorio[];\n otrosLaboratorios: OtroLaboratorio[];");
|
||||||
|
|
||||||
|
// add actions
|
||||||
|
const actionsRegex = /eliminarLaboratorio: \(id: string\) => Promise<void>;/;
|
||||||
|
code = code.replace(actionsRegex, "eliminarLaboratorio: (id: string) => Promise<void>;\n agregarOtroLaboratorio: (laboratorio: Omit<OtroLaboratorio, 'id'>) => Promise<void>;\n actualizarOtroLaboratorio: (id: string, datos: Partial<OtroLaboratorio>) => Promise<void>;\n eliminarOtroLaboratorio: (id: string) => Promise<void>;");
|
||||||
|
|
||||||
|
// add initial state
|
||||||
|
code = code.replace("laboratorios: [],", "laboratorios: [],\n otrosLaboratorios: [],");
|
||||||
|
|
||||||
|
// fetch action (loadState)
|
||||||
|
code = code.replace("laboratorios: data.laboratorios || [],", "laboratorios: data.laboratorios || [],\n otrosLaboratorios: data.otrosLaboratorios || [],");
|
||||||
|
|
||||||
|
// methods
|
||||||
|
const methodsString = `
|
||||||
|
eliminarLaboratorio: async (id) => {
|
||||||
|
try {
|
||||||
|
await api.delete(\`/laboratorios/\${id}\`);
|
||||||
|
set((state) => ({
|
||||||
|
laboratorios: state.laboratorios.filter((l) => l.id !== id),
|
||||||
|
}));
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error deleting laboratorio:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
agregarOtroLaboratorio: async (laboratorio) => {
|
||||||
|
try {
|
||||||
|
const response = await api.post('/otros-laboratorios', laboratorio);
|
||||||
|
set((state) => ({
|
||||||
|
otrosLaboratorios: [...state.otrosLaboratorios, response.data],
|
||||||
|
}));
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error adding otro laboratorio:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
actualizarOtroLaboratorio: async (id, datos) => {
|
||||||
|
try {
|
||||||
|
await api.put(\`/otros-laboratorios/\${id}\`, datos);
|
||||||
|
set((state) => ({
|
||||||
|
otrosLaboratorios: state.otrosLaboratorios.map((l) =>
|
||||||
|
l.id === id ? { ...l, ...datos } : l
|
||||||
|
),
|
||||||
|
}));
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error updating otro laboratorio:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
eliminarOtroLaboratorio: async (id) => {
|
||||||
|
try {
|
||||||
|
await api.delete(\`/otros-laboratorios/\${id}\`);
|
||||||
|
set((state) => ({
|
||||||
|
otrosLaboratorios: state.otrosLaboratorios.filter((l) => l.id !== id),
|
||||||
|
}));
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error deleting otro laboratorio:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
`;
|
||||||
|
|
||||||
|
const replaceMethods = `
|
||||||
|
eliminarLaboratorio: async (id) => {
|
||||||
|
try {
|
||||||
|
await api.delete(\`/laboratorios/\${id}\`);
|
||||||
|
set((state) => ({
|
||||||
|
laboratorios: state.laboratorios.filter((l) => l.id !== id),
|
||||||
|
}));
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error deleting laboratorio:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
`;
|
||||||
|
|
||||||
|
code = code.replace(replaceMethods.trim(), methodsString.trim());
|
||||||
|
fs.writeFileSync('src/hooks/useHospitalStore.ts', code);
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
const fs = require('fs');
|
||||||
|
let code = fs.readFileSync('src/hooks/useHospitalStore.ts', 'utf8');
|
||||||
|
|
||||||
|
const regexLab = /const eliminarLaboratorio = useCallback\(async \(id: string\) => \{[\s\S]*?\}\, \[state\.laboratorios\, apiCall\]\);/;
|
||||||
|
|
||||||
|
const addMethods = `
|
||||||
|
const agregarOtroLaboratorio = useCallback(async (otroLaboratorio: Omit<OtroLaboratorio, 'id'>) => {
|
||||||
|
try {
|
||||||
|
const { data } = await apiCall('POST', '/api/otros-laboratorios', otroLaboratorio);
|
||||||
|
if (data) {
|
||||||
|
setState(prev => ({ ...prev, otrosLaboratorios: [...prev.otrosLaboratorios, data] }));
|
||||||
|
return data.id;
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Error adding otro laboratorio', err);
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}, [apiCall]);
|
||||||
|
|
||||||
|
const actualizarOtroLaboratorio = useCallback(async (id: string, datos: Partial<OtroLaboratorio>) => {
|
||||||
|
try {
|
||||||
|
await apiCall('PUT', \`/api/otros-laboratorios/\${id}\`, datos);
|
||||||
|
setState(prev => ({
|
||||||
|
...prev,
|
||||||
|
otrosLaboratorios: prev.otrosLaboratorios.map(o => o.id === id ? { ...o, ...datos } : o)
|
||||||
|
}));
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Error updating otro laboratorio', err);
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}, [apiCall]);
|
||||||
|
|
||||||
|
const eliminarOtroLaboratorio = useCallback(async (id: string) => {
|
||||||
|
try {
|
||||||
|
await apiCall('DELETE', \`/api/otros-laboratorios/\${id}\`);
|
||||||
|
setState(prev => ({
|
||||||
|
...prev,
|
||||||
|
otrosLaboratorios: prev.otrosLaboratorios.filter(o => o.id !== id)
|
||||||
|
}));
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Error deleting otro laboratorio', err);
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}, [apiCall]);
|
||||||
|
`;
|
||||||
|
|
||||||
|
code = code.replace(regexLab, match => match + "\n\n" + addMethods);
|
||||||
|
|
||||||
|
const returnObjRegex = /return \{[\s\S]*?isLoaded,\s*\};/;
|
||||||
|
const match = code.match(returnObjRegex);
|
||||||
|
if (match) {
|
||||||
|
let newReturn = match[0].replace(
|
||||||
|
"eliminarLaboratorio,",
|
||||||
|
"eliminarLaboratorio,\n agregarOtroLaboratorio,\n actualizarOtroLaboratorio,\n eliminarOtroLaboratorio,"
|
||||||
|
);
|
||||||
|
code = code.replace(match[0], newReturn);
|
||||||
|
}
|
||||||
|
|
||||||
|
fs.writeFileSync('src/hooks/useHospitalStore.ts', code);
|
||||||
@@ -8,6 +8,7 @@ import { initDb,
|
|||||||
getAllInternaciones, getInternacionById, createInternacion, updateInternacion, deleteInternacion,
|
getAllInternaciones, getInternacionById, createInternacion, updateInternacion, deleteInternacion,
|
||||||
getAllEvoluciones, createEvolucion, updateEvolucion, deleteEvolucion,
|
getAllEvoluciones, createEvolucion, updateEvolucion, deleteEvolucion,
|
||||||
getAllLaboratorios, createLaboratorio, updateLaboratorio, deleteLaboratorio,
|
getAllLaboratorios, createLaboratorio, updateLaboratorio, deleteLaboratorio,
|
||||||
|
getAllOtrosLaboratorios, createOtroLaboratorio, updateOtroLaboratorio, deleteOtroLaboratorio,
|
||||||
getAllGlucemias, createGlucemia, updateGlucemia, deleteGlucemia,
|
getAllGlucemias, createGlucemia, updateGlucemia, deleteGlucemia,
|
||||||
getAllAcidosBase, createAcidoBase, updateAcidoBase, deleteAcidoBase,
|
getAllAcidosBase, createAcidoBase, updateAcidoBase, deleteAcidoBase,
|
||||||
getAllCultivos, createCultivo, updateCultivo, deleteCultivo,
|
getAllCultivos, createCultivo, updateCultivo, deleteCultivo,
|
||||||
@@ -90,6 +91,7 @@ app.get('/api/state', async (req, res) => {
|
|||||||
examenFisico: typeof e.examenFisico === 'string' ? JSON.parse(e.examenFisico) : e.examenFisico
|
examenFisico: typeof e.examenFisico === 'string' ? JSON.parse(e.examenFisico) : e.examenFisico
|
||||||
})),
|
})),
|
||||||
laboratorios: (await getAllLaboratorios()).map(l => ({ ...l, resultados: typeof l.resultados === 'string' ? JSON.parse(l.resultados) : l.resultados })),
|
laboratorios: (await getAllLaboratorios()).map(l => ({ ...l, resultados: typeof l.resultados === 'string' ? JSON.parse(l.resultados) : l.resultados })),
|
||||||
|
otrosLaboratorios: await getAllOtrosLaboratorios(),
|
||||||
glucemias: await getAllGlucemias(),
|
glucemias: await getAllGlucemias(),
|
||||||
acidosBase: await getAllAcidosBase(),
|
acidosBase: await getAllAcidosBase(),
|
||||||
cultivos: await getAllCultivos(),
|
cultivos: await getAllCultivos(),
|
||||||
@@ -412,6 +414,60 @@ app.delete('/api/laboratorios/:id', async (req, res) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
// ========== OTROS LABORATORIOS ==========
|
||||||
|
app.get('/api/otros-laboratorios', async (req, res) => {
|
||||||
|
try {
|
||||||
|
const records = await getAllOtrosLaboratorios();
|
||||||
|
res.json(records);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error fetching otros-laboratorios:', error);
|
||||||
|
res.status(500).json({ error: 'Failed to fetch otros-laboratorios' });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
app.post('/api/otros-laboratorios', async (req, res) => {
|
||||||
|
try {
|
||||||
|
const { pacienteId, fecha, observaciones } = req.body;
|
||||||
|
if (!pacienteId || !fecha || !observaciones) {
|
||||||
|
return res.status(400).json({ error: 'pacienteId, fecha and observaciones are required' });
|
||||||
|
}
|
||||||
|
|
||||||
|
const record = {
|
||||||
|
id: generateUUID(),
|
||||||
|
...req.body
|
||||||
|
};
|
||||||
|
|
||||||
|
const newRecord = await createOtroLaboratorio(record);
|
||||||
|
res.status(201).json(newRecord);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error creating otro laboratorio:', error);
|
||||||
|
res.status(500).json({ error: 'Failed to create otro laboratorio' });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
app.put('/api/otros-laboratorios/:id', async (req, res) => {
|
||||||
|
try {
|
||||||
|
const result = await updateOtroLaboratorio(req.params.id, req.body);
|
||||||
|
if (!result.success) return res.status(404).json({ error: 'Record not found' });
|
||||||
|
res.json(result);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error updating otro laboratorio:', error);
|
||||||
|
res.status(500).json({ error: 'Failed to update otro laboratorio' });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
app.delete('/api/otros-laboratorios/:id', async (req, res) => {
|
||||||
|
try {
|
||||||
|
const result = await deleteOtroLaboratorio(req.params.id);
|
||||||
|
if (!result.success) return res.status(404).json({ error: 'Record not found' });
|
||||||
|
res.json({ message: 'Deleted successfully' });
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error deleting otro laboratorio:', error);
|
||||||
|
res.status(500).json({ error: 'Failed to delete otro laboratorio' });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// ========== GLUCEMIAS ==========
|
// ========== GLUCEMIAS ==========
|
||||||
app.get('/api/glucemias', async (req, res) => {
|
app.get('/api/glucemias', async (req, res) => {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -56,6 +56,7 @@ const memStore = {
|
|||||||
internaciones: [],
|
internaciones: [],
|
||||||
evoluciones: [],
|
evoluciones: [],
|
||||||
laboratorios: [],
|
laboratorios: [],
|
||||||
|
otrosLaboratorios: [],
|
||||||
glucemias: [],
|
glucemias: [],
|
||||||
acidosbase: [],
|
acidosbase: [],
|
||||||
cultivos: [],
|
cultivos: [],
|
||||||
@@ -1058,3 +1059,63 @@ export async function importAllData(dump) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export async function getAllOtrosLaboratorios() {
|
||||||
|
if (db) {
|
||||||
|
const records = await db.collection('otros-laboratorios').find().toArray();
|
||||||
|
return cleanDocs(records);
|
||||||
|
}
|
||||||
|
return [...memStore.otrosLaboratorios];
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function createOtroLaboratorio(record) {
|
||||||
|
const doc = {
|
||||||
|
id: record.id,
|
||||||
|
pacienteId: record.pacienteId,
|
||||||
|
internacionId: record.internacionId,
|
||||||
|
fecha: record.fecha,
|
||||||
|
hora: record.hora,
|
||||||
|
observaciones: record.observaciones,
|
||||||
|
createdAt: new Date().toISOString()
|
||||||
|
};
|
||||||
|
|
||||||
|
if (db) {
|
||||||
|
await db.collection('otros-laboratorios').insertOne(doc);
|
||||||
|
return cleanDoc(doc);
|
||||||
|
}
|
||||||
|
|
||||||
|
memStore.otrosLaboratorios.push(doc);
|
||||||
|
return doc;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function updateOtroLaboratorio(id, updates) {
|
||||||
|
if (db) {
|
||||||
|
await db.collection('otros-laboratorios').updateOne(
|
||||||
|
{ id },
|
||||||
|
{ $set: { ...updates, updatedAt: new Date().toISOString() } }
|
||||||
|
);
|
||||||
|
return { success: true };
|
||||||
|
}
|
||||||
|
|
||||||
|
const idx = memStore.otrosLaboratorios.findIndex(x => x.id === id);
|
||||||
|
if (idx !== -1) {
|
||||||
|
memStore.otrosLaboratorios[idx] = { ...memStore.otrosLaboratorios[idx], ...updates, updatedAt: new Date().toISOString() };
|
||||||
|
return { success: true };
|
||||||
|
}
|
||||||
|
return { success: false };
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function deleteOtroLaboratorio(id) {
|
||||||
|
if (db) {
|
||||||
|
await db.collection('otros-laboratorios').deleteOne({ id });
|
||||||
|
return { success: true };
|
||||||
|
}
|
||||||
|
|
||||||
|
const idx = memStore.otrosLaboratorios.findIndex(x => x.id === id);
|
||||||
|
if (idx !== -1) {
|
||||||
|
memStore.otrosLaboratorios.splice(idx, 1);
|
||||||
|
return { success: true };
|
||||||
|
}
|
||||||
|
return { success: false };
|
||||||
|
}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import type {
|
|||||||
Grupo,
|
Grupo,
|
||||||
Internacion,
|
Internacion,
|
||||||
Evolucion,
|
Evolucion,
|
||||||
Laboratorio,
|
Laboratorio, OtroLaboratorio,
|
||||||
Glucemia,
|
Glucemia,
|
||||||
AcidoBase,
|
AcidoBase,
|
||||||
Cultivo,
|
Cultivo,
|
||||||
@@ -41,6 +41,7 @@ interface HospitalState {
|
|||||||
internaciones: Internacion[];
|
internaciones: Internacion[];
|
||||||
evoluciones: Evolucion[];
|
evoluciones: Evolucion[];
|
||||||
laboratorios: Laboratorio[];
|
laboratorios: Laboratorio[];
|
||||||
|
otrosLaboratorios: OtroLaboratorio[];
|
||||||
glucemias: Glucemia[];
|
glucemias: Glucemia[];
|
||||||
acidosBase: AcidoBase[];
|
acidosBase: AcidoBase[];
|
||||||
cultivos: Cultivo[];
|
cultivos: Cultivo[];
|
||||||
@@ -66,6 +67,7 @@ const defaultState = (): HospitalState => ({
|
|||||||
internaciones: [],
|
internaciones: [],
|
||||||
evoluciones: [],
|
evoluciones: [],
|
||||||
laboratorios: [],
|
laboratorios: [],
|
||||||
|
otrosLaboratorios: [],
|
||||||
glucemias: [],
|
glucemias: [],
|
||||||
acidosBase: [],
|
acidosBase: [],
|
||||||
cultivos: [],
|
cultivos: [],
|
||||||
@@ -744,6 +746,51 @@ const actualizarInternacion = useCallback(async (internacionId: string, datos: P
|
|||||||
}
|
}
|
||||||
}, [state, checkGrupoPermission, apiCall]);
|
}, [state, checkGrupoPermission, apiCall]);
|
||||||
|
|
||||||
|
// Acciones de otros laboratorios
|
||||||
|
const agregarOtroLaboratorio = useCallback(async (otroLaboratorio: Omit<OtroLaboratorio, 'id'>) => {
|
||||||
|
const nuevo: OtroLaboratorio = {
|
||||||
|
...otroLaboratorio,
|
||||||
|
id: generateUUID(),
|
||||||
|
};
|
||||||
|
try {
|
||||||
|
await apiCall('POST', '/api/otros-laboratorios', nuevo);
|
||||||
|
setState(prev => ({
|
||||||
|
...prev,
|
||||||
|
otrosLaboratorios: [...prev.otrosLaboratorios, nuevo],
|
||||||
|
}));
|
||||||
|
return nuevo.id;
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Error al agregar otro laboratorio:', err);
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}, [apiCall]);
|
||||||
|
|
||||||
|
const eliminarOtroLaboratorio = useCallback(async (id: string) => {
|
||||||
|
try {
|
||||||
|
await apiCall('DELETE', `/api/otros-laboratorios/${id}`);
|
||||||
|
setState(prev => ({
|
||||||
|
...prev,
|
||||||
|
otrosLaboratorios: prev.otrosLaboratorios.filter(o => o.id !== id),
|
||||||
|
}));
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Error al eliminar otro laboratorio:', err);
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}, [apiCall]);
|
||||||
|
|
||||||
|
const actualizarOtroLaboratorio = useCallback(async (id: string, datos: Partial<OtroLaboratorio>) => {
|
||||||
|
try {
|
||||||
|
await apiCall('PUT', `/api/otros-laboratorios/${id}`, datos);
|
||||||
|
setState(prev => ({
|
||||||
|
...prev,
|
||||||
|
otrosLaboratorios: prev.otrosLaboratorios.map(o => o.id === id ? { ...o, ...datos } : o),
|
||||||
|
}));
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Error al actualizar otro laboratorio:', err);
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}, [apiCall]);
|
||||||
|
|
||||||
// Acciones de glucemias
|
// Acciones de glucemias
|
||||||
const agregarGlucemia = useCallback(async (glucemia: Omit<Glucemia, 'id'>) => {
|
const agregarGlucemia = useCallback(async (glucemia: Omit<Glucemia, 'id'>) => {
|
||||||
const internacion = state.internaciones.find(i => i.id === glucemia.internacionId);
|
const internacion = state.internaciones.find(i => i.id === glucemia.internacionId);
|
||||||
@@ -1522,6 +1569,9 @@ const actualizarInternacion = useCallback(async (internacionId: string, datos: P
|
|||||||
agregarLaboratorio,
|
agregarLaboratorio,
|
||||||
actualizarLaboratorio,
|
actualizarLaboratorio,
|
||||||
eliminarLaboratorio,
|
eliminarLaboratorio,
|
||||||
|
agregarOtroLaboratorio,
|
||||||
|
actualizarOtroLaboratorio,
|
||||||
|
eliminarOtroLaboratorio,
|
||||||
agregarGlucemia,
|
agregarGlucemia,
|
||||||
actualizarGlucemia,
|
actualizarGlucemia,
|
||||||
eliminarGlucemia,
|
eliminarGlucemia,
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@
|
|||||||
import { ScrollArea, ScrollBar } from "@/components/ui/scroll-area"
|
import { ScrollArea, ScrollBar } from "@/components/ui/scroll-area"
|
||||||
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from '@/components/ui/dropdown-menu';
|
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from '@/components/ui/dropdown-menu';
|
||||||
import { LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer } from 'recharts';
|
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 type { Laboratorio, Glucemia, AcidoBase, Cultivo, Paciente, ResultadoLaboratorio, Internacion, Evolucion, Cama, EstudioComplementario, Interconsulta, ATB, Indicacion, MovimientoIndicacion, Pendiente, OtroLaboratorio } from '@/types';
|
||||||
import { formatDateDDMMYYYY, getLocalToday } from '@/lib/utils';
|
import { formatDateDDMMYYYY, getLocalToday } from '@/lib/utils';
|
||||||
import { SeccionIndicaciones } from './SeccionIndicaciones';
|
import { SeccionIndicaciones } from './SeccionIndicaciones';
|
||||||
|
|
||||||
@@ -251,7 +251,7 @@ export function HistoriaClinica({
|
|||||||
}: HistoriaClinicaProps) {
|
}: HistoriaClinicaProps) {
|
||||||
const [tabActivo, setTabActivo] = useState('evoluciones');
|
const [tabActivo, setTabActivo] = useState('evoluciones');
|
||||||
const [portalNode, setPortalNode] = useState<HTMLDivElement | null>(null);
|
const [portalNode, setPortalNode] = useState<HTMLDivElement | null>(null);
|
||||||
const { pendientes } = useHospitalStore();
|
const { pendientes, otrosLaboratorios, agregarOtroLaboratorio } = useHospitalStore();
|
||||||
const indicacionesActivas = (indicadores || []).filter(i => i.estado === 'Activa');
|
const indicacionesActivas = (indicadores || []).filter(i => i.estado === 'Activa');
|
||||||
const pendientesActivos = (pendientes || []).filter(p => p.pacienteId === paciente.id && p.estado === 'pendiente');
|
const pendientesActivos = (pendientes || []).filter(p => p.pacienteId === paciente.id && p.estado === 'pendiente');
|
||||||
|
|
||||||
@@ -568,7 +568,19 @@ export function HistoriaClinica({
|
|||||||
</TabsContent>
|
</TabsContent>
|
||||||
|
|
||||||
<TabsContent value="laboratorios" className="mt-4 min-w-0 w-full overflow-hidden">
|
<TabsContent value="laboratorios" className="mt-4 min-w-0 w-full overflow-hidden">
|
||||||
<SeccionLaboratorios portalNode={portalNode} lab={laboratorios} patientId={paciente.id} internacionId={internacion.id} add={onAgregarLaboratorio} update={onActualizarLaboratorio} del={onEliminarLaboratorio} addAcidoBase={onAgregarAcidoBase} canEdit={canEdit} />
|
<SeccionLaboratorios
|
||||||
|
portalNode={portalNode}
|
||||||
|
lab={laboratorios}
|
||||||
|
otrosLaboratorios={otrosLaboratorios}
|
||||||
|
patientId={paciente.id}
|
||||||
|
internacionId={internacion.id}
|
||||||
|
add={onAgregarLaboratorio}
|
||||||
|
update={onActualizarLaboratorio}
|
||||||
|
del={onEliminarLaboratorio}
|
||||||
|
addAcidoBase={onAgregarAcidoBase}
|
||||||
|
addOtroLaboratorio={agregarOtroLaboratorio}
|
||||||
|
canEdit={canEdit}
|
||||||
|
/>
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
|
|
||||||
<TabsContent value="evoluciones" className="mt-4">
|
<TabsContent value="evoluciones" className="mt-4">
|
||||||
@@ -925,20 +937,26 @@ function SeccionGlucemias({ glucemias, patientId, internacionId, add, update, de
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function SeccionLaboratorios({ lab, patientId, internacionId, add, update, del, addAcidoBase, canEdit, portalNode}: {
|
function SeccionLaboratorios({ lab, otrosLaboratorios = [], patientId, internacionId, add, update, del, addAcidoBase, addOtroLaboratorio, canEdit, portalNode}: {
|
||||||
lab: Laboratorio[];
|
lab: Laboratorio[];
|
||||||
|
otrosLaboratorios?: OtroLaboratorio[];
|
||||||
patientId: string;
|
patientId: string;
|
||||||
internacionId: string;
|
internacionId: string;
|
||||||
add: (l: Omit<Laboratorio, 'id'>) => void;
|
add: (l: Omit<Laboratorio, 'id'>) => void;
|
||||||
update: (id: string, data: Partial<Laboratorio>) => void;
|
update: (id: string, data: Partial<Laboratorio>) => void;
|
||||||
del: (id: string) => void;
|
del: (id: string) => void;
|
||||||
addAcidoBase?: (a: Omit<AcidoBase, 'id'>) => void;
|
addAcidoBase?: (a: Omit<AcidoBase, 'id'>) => void;
|
||||||
|
addOtroLaboratorio?: (o: Omit<OtroLaboratorio, 'id'>) => void;
|
||||||
canEdit?: boolean;
|
canEdit?: boolean;
|
||||||
portalNode?: HTMLDivElement | null;
|
portalNode?: HTMLDivElement | null;
|
||||||
}) {
|
}) {
|
||||||
const [dialog, setDialog] = useState(false);
|
const [dialog, setDialog] = useState(false);
|
||||||
const [obsDialog, setObsDialog] = useState(false);
|
const [obsDialog, setObsDialog] = useState(false);
|
||||||
|
|
||||||
const [otrosDialog, setOtrosDialog] = useState(false);
|
const [otrosDialog, setOtrosDialog] = useState(false);
|
||||||
|
const [nuevaObsFecha, setNuevaObsFecha] = useState(getLocalToday());
|
||||||
|
const [nuevaObsTexto, setNuevaObsTexto] = useState('');
|
||||||
|
|
||||||
const [evolDialog, setEvolDialog] = useState(false);
|
const [evolDialog, setEvolDialog] = useState(false);
|
||||||
const [edit, setEdit] = useState<Laboratorio | null>(null);
|
const [edit, setEdit] = useState<Laboratorio | null>(null);
|
||||||
const [obsLab, setObsLab] = useState<Laboratorio | null>(null);
|
const [obsLab, setObsLab] = useState<Laboratorio | null>(null);
|
||||||
@@ -1387,13 +1405,27 @@ function SeccionLaboratorios({ lab, patientId, internacionId, add, update, del,
|
|||||||
addAcidoBase({ ...importAcidoBase, pacienteId: patientId, internacionId, fecha: importFecha, hora: importHora });
|
addAcidoBase({ ...importAcidoBase, pacienteId: patientId, internacionId, fecha: importFecha, hora: importHora });
|
||||||
}
|
}
|
||||||
|
|
||||||
add({
|
if (importResultados.length > 0) {
|
||||||
pacienteId: patientId,
|
add({
|
||||||
fecha: importFecha,
|
pacienteId: patientId,
|
||||||
hora: importHora,
|
internacionId,
|
||||||
resultados: importResultados,
|
fecha: importFecha,
|
||||||
observaciones: importObservaciones
|
hora: importHora,
|
||||||
});
|
resultados: importResultados,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (importObservaciones && importObservaciones.trim() !== '') {
|
||||||
|
if (addOtroLaboratorio) {
|
||||||
|
addOtroLaboratorio({
|
||||||
|
pacienteId: patientId,
|
||||||
|
internacionId,
|
||||||
|
fecha: importFecha,
|
||||||
|
hora: importHora,
|
||||||
|
observaciones: importObservaciones
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
setImportDialog(false);
|
setImportDialog(false);
|
||||||
setImportTexto('');
|
setImportTexto('');
|
||||||
@@ -1457,9 +1489,9 @@ function SeccionLaboratorios({ lab, patientId, internacionId, add, update, del,
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="space-y-4 w-full max-w-full min-w-0">
|
<div className="space-y-4 w-full max-w-full min-w-0">
|
||||||
<div className="flex justify-between">
|
{portalNode ? createPortal(
|
||||||
<div className="flex gap-2">
|
<>
|
||||||
<DropdownMenu>
|
<DropdownMenu>
|
||||||
<DropdownMenuTrigger asChild>
|
<DropdownMenuTrigger asChild>
|
||||||
<Button variant="outline">
|
<Button variant="outline">
|
||||||
<List className="h-4 w-4 mr-2" />
|
<List className="h-4 w-4 mr-2" />
|
||||||
@@ -1478,15 +1510,43 @@ function SeccionLaboratorios({ lab, patientId, internacionId, add, update, del,
|
|||||||
<TrendingUp className="h-4 w-4 mr-2" />
|
<TrendingUp className="h-4 w-4 mr-2" />
|
||||||
Evolución
|
Evolución
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
<DropdownMenuItem onClick={() => setOtrosDialog(true)}>
|
|
||||||
<Eye className="h-4 w-4 mr-2" />
|
|
||||||
Ver Otros
|
|
||||||
</DropdownMenuItem>
|
|
||||||
</DropdownMenuContent>
|
</DropdownMenuContent>
|
||||||
</DropdownMenu>
|
</DropdownMenu>
|
||||||
|
<Button variant="outline" onClick={() => setOtrosDialog(true)}><Eye className="h-4 w-4 mr-2" />Otros</Button>
|
||||||
<Button variant="outline" onClick={() => { setImportFecha(getLocalToday()); setImportHora(new Date().toTimeString().slice(0, 5)); setImportTexto(''); setImportResultados([]); setImportObservaciones(''); setImportDialog(true); }}><FileText className="h-4 w-4 mr-2" />Importar</Button>
|
<Button variant="outline" onClick={() => { setImportFecha(getLocalToday()); setImportHora(new Date().toTimeString().slice(0, 5)); setImportTexto(''); setImportResultados([]); setImportObservaciones(''); setImportDialog(true); }}><FileText className="h-4 w-4 mr-2" />Importar</Button>
|
||||||
|
</>,
|
||||||
|
portalNode
|
||||||
|
) : (
|
||||||
|
<div className="flex justify-between mb-4">
|
||||||
|
<div className="flex gap-2">
|
||||||
|
<DropdownMenu>
|
||||||
|
<DropdownMenuTrigger asChild>
|
||||||
|
<Button variant="outline">
|
||||||
|
<List className="h-4 w-4 mr-2" />
|
||||||
|
Acciones
|
||||||
|
<ChevronDown className="h-4 w-4 ml-2" />
|
||||||
|
</Button>
|
||||||
|
</DropdownMenuTrigger>
|
||||||
|
<DropdownMenuContent align="start" className="w-48">
|
||||||
|
{canEdit && (
|
||||||
|
<DropdownMenuItem onClick={() => { reset(); setDialog(true); }}>
|
||||||
|
<Plus className="h-4 w-4 mr-2" />
|
||||||
|
Nuevo
|
||||||
|
</DropdownMenuItem>
|
||||||
|
)}
|
||||||
|
<DropdownMenuItem onClick={() => { setParametroEvolucion('Hematocrito'); setEvolDialog(true); }}>
|
||||||
|
<TrendingUp className="h-4 w-4 mr-2" />
|
||||||
|
Evolución
|
||||||
|
</DropdownMenuItem>
|
||||||
|
|
||||||
|
</DropdownMenuContent>
|
||||||
|
</DropdownMenu>
|
||||||
|
<Button variant="outline" onClick={() => setOtrosDialog(true)}><Eye className="h-4 w-4 mr-2" />Otros</Button>
|
||||||
|
<Button variant="outline" onClick={() => { setImportFecha(getLocalToday()); setImportHora(new Date().toTimeString().slice(0, 5)); setImportTexto(''); setImportResultados([]); setImportObservaciones(''); setImportDialog(true); }}><FileText className="h-4 w-4 mr-2" />Importar</Button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
)}
|
||||||
<Dialog open={dialog} onOpenChange={setDialog}>
|
<Dialog open={dialog} onOpenChange={setDialog}>
|
||||||
<DialogContent className="sm:max-w-2xl max-h-[90vh] overflow-y-auto">
|
<DialogContent className="sm:max-w-2xl max-h-[90vh] overflow-y-auto">
|
||||||
<DialogHeader><DialogTitle>{edit ? 'Editar' : 'Nuevo'} Laboratorio</DialogTitle></DialogHeader>
|
<DialogHeader><DialogTitle>{edit ? 'Editar' : 'Nuevo'} Laboratorio</DialogTitle></DialogHeader>
|
||||||
@@ -1564,26 +1624,79 @@ function SeccionLaboratorios({ lab, patientId, internacionId, add, update, del,
|
|||||||
</Dialog>
|
</Dialog>
|
||||||
|
|
||||||
<Dialog open={otrosDialog} onOpenChange={setOtrosDialog}>
|
<Dialog open={otrosDialog} onOpenChange={setOtrosDialog}>
|
||||||
<DialogContent className="sm:max-w-xl max-h-[85vh] overflow-y-auto" aria-describedby={undefined}>
|
<DialogContent className="sm:max-w-2xl max-h-[85vh] overflow-y-auto">
|
||||||
<DialogHeader>
|
<DialogHeader>
|
||||||
<DialogTitle>Otros / Observaciones de Laboratorio</DialogTitle>
|
<DialogTitle>Otros / Observaciones de Laboratorio</DialogTitle>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
<div className="space-y-4 my-2">
|
<div className="space-y-4 my-2">
|
||||||
{labsConObservaciones.length === 0 ? (
|
|
||||||
<p className="text-sm text-gray-500 text-center py-6">No hay observaciones registradas.</p>
|
{canEdit && (
|
||||||
) : (
|
<div className="bg-gray-50 dark:bg-gray-900 p-4 rounded-lg border border-gray-200 dark:border-gray-700 space-y-3 mb-6">
|
||||||
labsConObservaciones.map(l => (
|
<h4 className="text-sm font-semibold text-gray-700 dark:text-gray-300">Agregar Nuevo Registro (Otros)</h4>
|
||||||
<div key={l.id} className="p-3 bg-gray-50 dark:bg-gray-800 rounded-lg border border-gray-100 dark:border-gray-700 space-y-2">
|
<div className="flex flex-col sm:flex-row gap-3">
|
||||||
<div className="flex justify-between items-center text-xs text-gray-500 dark:text-gray-400 font-medium">
|
<div className="w-full sm:w-1/3">
|
||||||
<span>{formatDateDDMMYYYY(l.fecha)} {l.hora || ''}</span>
|
<Label>Fecha</Label>
|
||||||
<Badge variant="secondary" className="text-[10px] px-1.5 py-0.5">Obs</Badge>
|
<Input type="date" value={nuevaObsFecha} onChange={e => setNuevaObsFecha(e.target.value)} />
|
||||||
|
</div>
|
||||||
|
<div className="w-full sm:w-2/3">
|
||||||
|
<Label>Observaciones / Determinaciones</Label>
|
||||||
|
<Textarea
|
||||||
|
placeholder="Ingrese los detalles, resultados, etc..."
|
||||||
|
value={nuevaObsTexto}
|
||||||
|
onChange={e => setNuevaObsTexto(e.target.value)}
|
||||||
|
className="min-h-[80px]"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<p className="text-sm text-gray-700 dark:text-gray-300 whitespace-pre-wrap font-sans">{l.observaciones}</p>
|
|
||||||
</div>
|
</div>
|
||||||
))
|
<div className="flex justify-end">
|
||||||
|
<Button onClick={() => {
|
||||||
|
if (addOtroLaboratorio && nuevaObsTexto.trim() !== '') {
|
||||||
|
addOtroLaboratorio({
|
||||||
|
pacienteId: patientId,
|
||||||
|
internacionId,
|
||||||
|
fecha: nuevaObsFecha,
|
||||||
|
hora: new Date().toTimeString().slice(0, 5),
|
||||||
|
observaciones: nuevaObsTexto
|
||||||
|
});
|
||||||
|
setNuevaObsTexto('');
|
||||||
|
setNuevaObsFecha(getLocalToday());
|
||||||
|
toast.success('Registro guardado correctamente');
|
||||||
|
}
|
||||||
|
}} disabled={!nuevaObsTexto.trim()}>
|
||||||
|
<Save className="h-4 w-4 mr-2" /> Guardar
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<h4 className="text-sm font-semibold text-gray-700 dark:text-gray-300 mb-3 border-b pb-2">Registros</h4>
|
||||||
|
<div className="space-y-3 max-h-[40vh] overflow-y-auto pr-2">
|
||||||
|
{[
|
||||||
|
...labsConObservaciones.map(l => ({ id: l.id, fecha: l.fecha, hora: l.hora, observaciones: l.observaciones, source: 'lab' })),
|
||||||
|
...(otrosLaboratorios || []).filter(o => o.pacienteId === patientId).map(o => ({ id: o.id, fecha: o.fecha, hora: o.hora, observaciones: o.observaciones, source: 'otros' }))
|
||||||
|
].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;
|
||||||
|
}).map((item, idx) => (
|
||||||
|
<div key={item.id + idx} className="p-3 bg-gray-50 dark:bg-gray-800 rounded-lg border border-gray-100 dark:border-gray-700 space-y-2">
|
||||||
|
<div className="flex justify-between items-center text-xs text-gray-500 dark:text-gray-400 font-medium">
|
||||||
|
<span>{formatDateDDMMYYYY(item.fecha)} {item.hora || ''}</span>
|
||||||
|
<Badge variant="secondary" className="text-[10px] px-1.5 py-0.5">{item.source === 'lab' ? 'Obs. de Lab' : 'Otros'}</Badge>
|
||||||
|
</div>
|
||||||
|
<p className="text-sm text-gray-700 dark:text-gray-300 whitespace-pre-wrap font-sans">{item.observaciones}</p>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
|
||||||
|
{(labsConObservaciones.length === 0 && (!otrosLaboratorios || otrosLaboratorios.filter(o => o.pacienteId === patientId).length === 0)) && (
|
||||||
|
<p className="text-sm text-gray-500 text-center py-6">No hay registros adicionales.</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<div className="flex justify-end gap-2 mt-4">
|
<div className="flex justify-end gap-2 mt-2">
|
||||||
<Button onClick={() => setOtrosDialog(false)}>Cerrar</Button>
|
<Button onClick={() => setOtrosDialog(false)}>Cerrar</Button>
|
||||||
</div>
|
</div>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
|
|||||||
@@ -119,6 +119,15 @@ export interface ResultadoLaboratorio {
|
|||||||
estado: 'Normal' | 'Alto' | 'Bajo' | 'Crítico';
|
estado: 'Normal' | 'Alto' | 'Bajo' | 'Crítico';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface OtroLaboratorio {
|
||||||
|
id: string;
|
||||||
|
pacienteId: string;
|
||||||
|
internacionId?: string;
|
||||||
|
fecha: string;
|
||||||
|
hora?: string;
|
||||||
|
observaciones: string;
|
||||||
|
}
|
||||||
|
|
||||||
export interface Glucemia {
|
export interface Glucemia {
|
||||||
id: string;
|
id: string;
|
||||||
pacienteId: string;
|
pacienteId: string;
|
||||||
|
|||||||
@@ -0,0 +1,79 @@
|
|||||||
|
const fs = require('fs');
|
||||||
|
let code = fs.readFileSync('src/sections/HistoriaClinica.tsx', 'utf8');
|
||||||
|
|
||||||
|
// 1. Update imports
|
||||||
|
code = code.replace("EstudioComplementario, Interconsulta, ATB, Indicacion, MovimientoIndicacion, Pendiente } from '@/types';", "EstudioComplementario, Interconsulta, ATB, Indicacion, MovimientoIndicacion, Pendiente, OtroLaboratorio } from '@/types';");
|
||||||
|
|
||||||
|
// 2. Destructure in component
|
||||||
|
const destructureRegex = /const \{[\s\S]*?\} = useHospitalStore\(\);/;
|
||||||
|
const destructuredMatch = code.match(destructureRegex);
|
||||||
|
if (destructuredMatch) {
|
||||||
|
let newDestructure = destructuredMatch[0].replace(
|
||||||
|
"laboratorios,",
|
||||||
|
"laboratorios, otrosLaboratorios, agregarOtroLaboratorio, actualizarOtroLaboratorio, eliminarOtroLaboratorio,"
|
||||||
|
);
|
||||||
|
code = code.replace(destructuredMatch[0], newDestructure);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Update SeccionLaboratorios props in main render
|
||||||
|
code = code.replace(
|
||||||
|
/<SeccionLaboratorios\s*portalNode=\{portalNode\}\s*lab=\{laboratorios\}\s*patientId=\{paciente\.id\}\s*internacionId=\{internacion\.id\}\s*add=\{onAgregarLaboratorio\}\s*update=\{onActualizarLaboratorio\}\s*del=\{onEliminarLaboratorio\}\s*addAcidoBase=\{onAgregarAcidoBase\}\s*canEdit=\{canEdit\}\s*\/>/,
|
||||||
|
`<SeccionLaboratorios
|
||||||
|
portalNode={portalNode}
|
||||||
|
lab={laboratorios}
|
||||||
|
otrosLaboratorios={otrosLaboratorios}
|
||||||
|
patientId={paciente.id}
|
||||||
|
internacionId={internacion.id}
|
||||||
|
add={onAgregarLaboratorio}
|
||||||
|
update={onActualizarLaboratorio}
|
||||||
|
del={onEliminarLaboratorio}
|
||||||
|
addAcidoBase={onAgregarAcidoBase}
|
||||||
|
addOtroLaboratorio={agregarOtroLaboratorio}
|
||||||
|
canEdit={canEdit}
|
||||||
|
/>`
|
||||||
|
);
|
||||||
|
|
||||||
|
// 4. In `handleImportarLaboratorio` (we need to change how observations are saved)
|
||||||
|
// Find the exact handleImportarLaboratorio function
|
||||||
|
const handleImportarMatch = code.match(/const handleImportarLaboratorio = \(\) => \{[\s\S]*?setImportHora\(new Date\(\)\.toTimeString\(\)\.slice\(0, 5\)\);\s*\};/);
|
||||||
|
if (handleImportarMatch) {
|
||||||
|
const newImport = `const handleImportarLaboratorio = () => {
|
||||||
|
if (!addAcidoBase) return;
|
||||||
|
if (importResultados.length === 0 && !importObservaciones && !importAcidoBase) return;
|
||||||
|
|
||||||
|
if (importAcidoBase) {
|
||||||
|
addAcidoBase({ ...importAcidoBase, pacienteId: patientId, internacionId, fecha: importFecha, hora: importHora });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (importResultados.length > 0) {
|
||||||
|
add({
|
||||||
|
pacienteId: patientId,
|
||||||
|
internacionId,
|
||||||
|
fecha: importFecha,
|
||||||
|
hora: importHora,
|
||||||
|
resultados: importResultados,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (importObservaciones && importObservaciones.trim() !== '') {
|
||||||
|
agregarOtroLaboratorio({
|
||||||
|
pacienteId: patientId,
|
||||||
|
internacionId,
|
||||||
|
fecha: importFecha,
|
||||||
|
hora: importHora,
|
||||||
|
observaciones: importObservaciones
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
setImportDialog(false);
|
||||||
|
setImportTexto('');
|
||||||
|
setImportResultados([]);
|
||||||
|
setImportObservaciones('');
|
||||||
|
setImportAcidoBase(null);
|
||||||
|
setImportFecha(getLocalToday());
|
||||||
|
setImportHora(new Date().toTimeString().slice(0, 5));
|
||||||
|
};`;
|
||||||
|
code = code.replace(handleImportarMatch[0], newImport);
|
||||||
|
}
|
||||||
|
|
||||||
|
fs.writeFileSync('src/sections/HistoriaClinica.tsx', code);
|
||||||
@@ -0,0 +1,110 @@
|
|||||||
|
const fs = require('fs');
|
||||||
|
let code = fs.readFileSync('src/sections/HistoriaClinica.tsx', 'utf8');
|
||||||
|
|
||||||
|
// Add states for new OtroLaboratorio
|
||||||
|
const newStates = `
|
||||||
|
const [otrosDialog, setOtrosDialog] = useState(false);
|
||||||
|
const [nuevaObsFecha, setNuevaObsFecha] = useState(getLocalToday());
|
||||||
|
const [nuevaObsTexto, setNuevaObsTexto] = useState('');
|
||||||
|
`;
|
||||||
|
|
||||||
|
code = code.replace("const [otrosDialog, setOtrosDialog] = useState(false);", newStates);
|
||||||
|
|
||||||
|
// Inside the portalNode or regular buttons, add "Otros" button.
|
||||||
|
// But we already have the DropdownMenu. Let's just add the "Otros" button inside the `flex gap-2` where Importar is.
|
||||||
|
const importBtnRegex = /<Button variant="outline" onClick=\{\(\) => \{ setImportFecha\(getLocalToday\(\)\); setImportHora\(new Date\(\)\.toTimeString\(\)\.slice\(0, 5\)\); setImportTexto\(''\); setImportResultados\(\[\]\); setImportObservaciones\(''\); setImportDialog\(true\); \}\}>\s*<FileText className="h-4 w-4 mr-2" \/>Importar\s*<\/Button>/g;
|
||||||
|
|
||||||
|
const newBtns = `<Button variant="outline" onClick={() => setOtrosDialog(true)}><Eye className="h-4 w-4 mr-2" />Otros</Button>\n <Button variant="outline" onClick={() => { setImportFecha(getLocalToday()); setImportHora(new Date().toTimeString().slice(0, 5)); setImportTexto(''); setImportResultados([]); setImportObservaciones(''); setImportDialog(true); }}><FileText className="h-4 w-4 mr-2" />Importar</Button>`;
|
||||||
|
|
||||||
|
code = code.replace(importBtnRegex, newBtns);
|
||||||
|
|
||||||
|
// Also remove the "Ver Otros" from Dropdown if it is there
|
||||||
|
code = code.replace(/<DropdownMenuItem onClick=\{\(\) => setOtrosDialog\(true\)\}>\s*<Eye className="h-4 w-4 mr-2" \/>\s*Ver Otros\s*<\/DropdownMenuItem>/g, '');
|
||||||
|
|
||||||
|
// Now we need to merge the logic for the dialog
|
||||||
|
const renderOtrosDialogRegex = /<Dialog open=\{otrosDialog\} onOpenChange=\{setOtrosDialog\}>[\s\S]*?<\/Dialog>/;
|
||||||
|
|
||||||
|
const newDialog = `
|
||||||
|
<Dialog open={otrosDialog} onOpenChange={setOtrosDialog}>
|
||||||
|
<DialogContent className="sm:max-w-2xl max-h-[85vh] overflow-y-auto">
|
||||||
|
<DialogHeader>
|
||||||
|
<DialogTitle>Otros / Observaciones de Laboratorio</DialogTitle>
|
||||||
|
</DialogHeader>
|
||||||
|
<div className="space-y-4 my-2">
|
||||||
|
|
||||||
|
{canEdit && (
|
||||||
|
<div className="bg-gray-50 dark:bg-gray-900 p-4 rounded-lg border border-gray-200 dark:border-gray-700 space-y-3 mb-6">
|
||||||
|
<h4 className="text-sm font-semibold text-gray-700 dark:text-gray-300">Agregar Nuevo Registro (Otros)</h4>
|
||||||
|
<div className="flex flex-col sm:flex-row gap-3">
|
||||||
|
<div className="w-full sm:w-1/3">
|
||||||
|
<Label>Fecha</Label>
|
||||||
|
<Input type="date" value={nuevaObsFecha} onChange={e => setNuevaObsFecha(e.target.value)} />
|
||||||
|
</div>
|
||||||
|
<div className="w-full sm:w-2/3">
|
||||||
|
<Label>Observaciones / Determinaciones</Label>
|
||||||
|
<Textarea
|
||||||
|
placeholder="Ingrese los detalles, resultados, etc..."
|
||||||
|
value={nuevaObsTexto}
|
||||||
|
onChange={e => setNuevaObsTexto(e.target.value)}
|
||||||
|
className="min-h-[80px]"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex justify-end">
|
||||||
|
<Button onClick={() => {
|
||||||
|
if (addOtroLaboratorio && nuevaObsTexto.trim() !== '') {
|
||||||
|
addOtroLaboratorio({
|
||||||
|
pacienteId: patientId,
|
||||||
|
internacionId,
|
||||||
|
fecha: nuevaObsFecha,
|
||||||
|
hora: new Date().toTimeString().slice(0, 5),
|
||||||
|
observaciones: nuevaObsTexto
|
||||||
|
});
|
||||||
|
setNuevaObsTexto('');
|
||||||
|
setNuevaObsFecha(getLocalToday());
|
||||||
|
toast.success('Registro guardado correctamente');
|
||||||
|
}
|
||||||
|
}} disabled={!nuevaObsTexto.trim()}>
|
||||||
|
<Save className="h-4 w-4 mr-2" /> Guardar
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<h4 className="text-sm font-semibold text-gray-700 dark:text-gray-300 mb-3 border-b pb-2">Registros</h4>
|
||||||
|
<div className="space-y-3 max-h-[40vh] overflow-y-auto pr-2">
|
||||||
|
{[
|
||||||
|
...labsConObservaciones.map(l => ({ id: l.id, fecha: l.fecha, hora: l.hora, observaciones: l.observaciones, source: 'lab' })),
|
||||||
|
...(otrosLaboratorios || []).filter(o => o.pacienteId === patientId).map(o => ({ id: o.id, fecha: o.fecha, hora: o.hora, observaciones: o.observaciones, source: 'otros' }))
|
||||||
|
].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;
|
||||||
|
}).map((item, idx) => (
|
||||||
|
<div key={item.id + idx} className="p-3 bg-gray-50 dark:bg-gray-800 rounded-lg border border-gray-100 dark:border-gray-700 space-y-2">
|
||||||
|
<div className="flex justify-between items-center text-xs text-gray-500 dark:text-gray-400 font-medium">
|
||||||
|
<span>{formatDateDDMMYYYY(item.fecha)} {item.hora || ''}</span>
|
||||||
|
<Badge variant="secondary" className="text-[10px] px-1.5 py-0.5">{item.source === 'lab' ? 'Obs. de Lab' : 'Otros'}</Badge>
|
||||||
|
</div>
|
||||||
|
<p className="text-sm text-gray-700 dark:text-gray-300 whitespace-pre-wrap font-sans">{item.observaciones}</p>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
|
||||||
|
{(labsConObservaciones.length === 0 && (!otrosLaboratorios || otrosLaboratorios.filter(o => o.pacienteId === patientId).length === 0)) && (
|
||||||
|
<p className="text-sm text-gray-500 text-center py-6">No hay registros adicionales.</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div className="flex justify-end gap-2 mt-2">
|
||||||
|
<Button onClick={() => setOtrosDialog(false)}>Cerrar</Button>
|
||||||
|
</div>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
|
`;
|
||||||
|
|
||||||
|
code = code.replace(renderOtrosDialogRegex, newDialog.trim());
|
||||||
|
|
||||||
|
fs.writeFileSync('src/sections/HistoriaClinica.tsx', code);
|
||||||
Reference in New Issue
Block a user