import express from 'express'; import cors from 'cors'; import { getDb, getValue, setValue, getUsuarioByDni, getUsuarioById, getAllUsuarios, createUsuario, updateUsuario, deleteUsuario, verifyPassword, hashPassword, getAllPacientes, createPaciente, updatePaciente, deletePaciente, getAllGrupos, createGrupo, updateGrupo, deleteGrupo, getAllCamas, createCama, updateCama, deleteCama, getAllInternaciones, createInternacion, updateInternacion, deleteInternacion, getAllEvoluciones, createEvolucion, updateEvolucion, deleteEvolucion, getAllLaboratorios, createLaboratorio, updateLaboratorio, deleteLaboratorio, getAllGlucemias, createGlucemia, updateGlucemia, deleteGlucemia, getAllAcidosBase, createAcidoBase, updateAcidoBase, deleteAcidoBase, getAllCultivos, createCultivo, updateCultivo, deleteCultivo, getAllEstudiosComplementarios, createEstudioComplementario, updateEstudioComplementario, deleteEstudioComplementario, getAllInterconsultas, createInterconsulta, updateInterconsulta, deleteInterconsulta, getAllAtb, createAtb, updateAtb, deleteAtb, getAllIndicaciones, createIndicacion, updateIndicacion, deleteIndicacion, getAllMovimientosIndicaciones, createMovimientoIndicacion } from './db.js'; const app = express(); app.use(cors()); app.use(express.json({ limit: '5mb' })); const PORT = process.env.PORT || 4001; const STORAGE_KEY = 'hospital-data-v1'; app.get('/api/state', (req, res) => { try { const gruposList = getAllGrupos(); const state = { pacientes: getAllPacientes(), grupos: gruposList, areas: gruposList, camas: getAllCamas(), internaciones: getAllInternaciones(), evoluciones: getAllEvoluciones(), laboratorios: getAllLaboratorios(), glucemias: getAllGlucemias(), acidosBase: getAllAcidosBase(), cultivos: getAllCultivos(), estudiosComplementarios: getAllEstudiosComplementarios(), interconsultas: getAllInterconsultas(), atb: getAllAtb(), indicaciones: getAllIndicaciones(), movimientosIndicaciones: getAllMovimientosIndicaciones(), vistaActual: 'dashboard', currentInternacionId: null }; res.json(state); } catch (err) { console.error(err); res.status(500).json({ error: 'failed to read state' }); } }); app.put('/api/state', (req, res) => { try { const state = req.body; const db = getDb(); // Use transaction for atomicity and better performance const result = db.transaction(() => { db.prepare('DELETE FROM evoluciones').run(); db.prepare('DELETE FROM acidosbase').run(); db.prepare('DELETE FROM cultivos').run(); db.prepare('DELETE FROM laboratorios').run(); db.prepare('DELETE FROM glucemias').run(); db.prepare('DELETE FROM internaciones').run(); db.prepare('DELETE FROM camas').run(); db.prepare('DELETE FROM grupos').run(); db.prepare('DELETE FROM areas').run(); db.prepare('DELETE FROM pacientes').run(); db.prepare('DELETE FROM estudiosComplementarios').run(); db.prepare('DELETE FROM interconsultas').run(); db.prepare('DELETE FROM atb').run(); db.prepare('DELETE FROM indicaciones').run(); db.prepare('DELETE FROM movimientos_indicaciones').run(); if (state.pacientes?.length) { const stmt = db.prepare('INSERT INTO pacientes (id, apellido, nombre, dni, fechaNacimiento, sexo, telefono, email, direccion, obraSocial, nacionalidad, medicacionHabitual, antecedentes, alergias, grupoSanguineo, historiaClinica) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)'); for (const p of state.pacientes) { stmt.run(p.id, p.apellido, p.nombre, p.dni, p.fechaNacimiento || null, p.sexo || null, p.telefono || null, p.email || null, p.direccion || null, p.obraSocial || null, p.nacionalidad || null, p.medicacionHabitual || null, p.antecedentes || null, p.alergias || null, p.grupoSanguineo || null, p.historiaClinica || null); } } const gruposData = state.grupos?.length ? state.grupos : state.areas; if (gruposData?.length) { const stmtG = db.prepare('INSERT OR IGNORE INTO grupos (id, nombre) VALUES (?, ?)'); const stmtA = db.prepare('INSERT OR IGNORE INTO areas (id, nombre) VALUES (?, ?)'); for (const g of gruposData) { stmtG.run(g.id, g.nombre); stmtA.run(g.id, g.nombre); } } if (state.camas?.length) { const stmt = db.prepare('INSERT OR IGNORE INTO camas (id, numero, grupoId, areaId, tipo, estado, sector) VALUES (?, ?, ?, ?, ?, ?, ?)'); for (const c of state.camas) { const gId = c.grupoId || c.areaId || null; const sector = computeSector(c.numero); stmt.run(c.id, c.numero, gId, gId, c.tipo, c.estado, sector); } } if (state.internaciones?.length) { const stmt = db.prepare('INSERT OR IGNORE INTO internaciones (id, pacienteId, camaId, grupoId, areaId, fechaIngresoHospital, fechaIngresoClinica, fechaEgreso, diagnosticoIngreso, motivoConsulta, enfermedadActual, antecedentesEnfermedadActual, diagnosticoEgreso, medicoIngresante, motivoEgreso, activa, apache, derivacion) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)'); for (const i of state.internaciones) { const gId = i.grupoId || i.areaId || null; stmt.run(i.id, i.pacienteId, i.camaId, gId, gId, i.fechaIngresoHospital || null, i.fechaIngresoClinica || null, i.fechaEgreso || null, i.diagnosticoIngreso || null, i.motivoConsulta || null, i.enfermedadActual || null, i.antecedentesEnfermedadActual || null, i.diagnosticoEgreso || null, i.medicoIngresante || null, i.motivoEgreso || null, i.activa ? 1 : 0, i.apache || null, i.derivacion || null); } } if (state.evoluciones?.length) { const stmt = db.prepare('INSERT OR IGNORE INTO evoluciones (id, internacionId, fecha, hora, medico, signosVitales, examenFisico, novedades, comentarios, pendientes) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)'); for (const e of state.evoluciones) { stmt.run(e.id, e.internacionId, e.fecha, e.hora || '', e.medico || '', JSON.stringify(e.signosVitales || {}), JSON.stringify(e.examenFisico || {}), e.novedades || '', e.comentarios || '', e.pendientes || ''); } } if (state.laboratorios?.length) { const stmt = db.prepare('INSERT INTO laboratorios (id, pacienteId, fecha, hora, tipo, resultados, observaciones) VALUES (?, ?, ?, ?, ?, ?, ?)'); for (const l of state.laboratorios) { stmt.run(l.id, l.pacienteId, l.fecha, l.hora || null, l.tipo, JSON.stringify(l.resultados), l.observaciones || null); } } if (state.glucemias?.length) { const stmt = db.prepare('INSERT INTO glucemias (id, pacienteId, internacionId, fecha, hora, valor, correccion) VALUES (?, ?, ?, ?, ?, ?, ?)'); for (const g of state.glucemias) { stmt.run(g.id, g.pacienteId, g.internacionId || null, g.fecha, g.hora || null, g.valor, g.correccion); } } if (state.acidosBase?.length) { const stmt = db.prepare('INSERT INTO acidosbase (id, pacienteId, fecha, hora, ph, pco2, po2, hco3, be, sato2, lactato, interpretacion, fio2) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)'); for (const a of state.acidosBase) { stmt.run(a.id, a.pacienteId, a.fecha, a.hora, a.ph, a.pco2, a.po2, a.hco3, a.be, a.sato2, a.lactato || null, a.interpretacion || null, a.fio2 || null); } } if (state.cultivos?.length) { const stmt = db.prepare('INSERT INTO cultivos (id, pacienteId, internacionId, fechaToma, protocolo, tipoMuestra, observaciones, estado, fechaResultado, germen, sensible, resistente) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)'); for (const c of state.cultivos) { stmt.run(c.id, c.pacienteId, c.internacionId, c.fechaToma, c.protocolo, c.tipoMuestra, c.observaciones, c.estado, c.fechaResultado, c.germen, c.sensible, c.resistente); } } if (state.estudiosComplementarios?.length) { const stmt = db.prepare('INSERT INTO estudiosComplementarios (id, pacienteId, internacionId, fecha, tipo, resultado) VALUES (?, ?, ?, ?, ?, ?)'); for (const e of state.estudiosComplementarios) { stmt.run(e.id, e.pacienteId, e.internacionId || null, e.fecha, e.tipo, e.resultado); } } if (state.interconsultas?.length) { const stmt = db.prepare('INSERT INTO interconsultas (id, pacienteId, internacionId, fecha, servicioInterconsultado, motivo, respuestaInterconsulta, respuestaFecha) VALUES (?, ?, ?, ?, ?, ?, ?, ?)'); for (const ic of state.interconsultas) { stmt.run(ic.id, ic.pacienteId, ic.internacionId, ic.fecha, ic.servicioInterconsultado, ic.motivo || null, ic.respuestaInterconsulta || null, ic.respuestaFecha || null); } } if (state.atb?.length) { const stmt = db.prepare('INSERT INTO atb (id, pacienteId, internacionId, antibiotico, fechaInicio, fechaFinalizacion) VALUES (?, ?, ?, ?, ?, ?)'); for (const atb of state.atb) { stmt.run(atb.id, atb.pacienteId, atb.internacionId, atb.antibiotico, atb.fechaInicio, atb.fechaFinalizacion || null); } } if (state.indicaciones?.length) { const stmt = db.prepare('INSERT INTO indicaciones (id, internacionId, tipo, droga, dosis, frecuenciaHoras, via, tipoPlan, tipoPlan2, cantidadMl, cantidadMl2, tiempoHoras, estado, medicoCrea, fechaCrea, tipoInsulina, unidadesDesayuno, unidadesAlmuerzo, unidadesNoche, indicacionNoFco) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)'); 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.tipoPlan || null, ind.tipoPlan2 || null, ind.cantidadMl || null, ind.cantidadMl2 || null, ind.tiempoHoras || null, ind.estado, ind.medicoCrea, ind.fechaCrea, ind.tipoInsulina || null, ind.unidadesDesayuno || null, ind.unidadesAlmuerzo || null, ind.unidadesNoche || null, ind.indicacionNoFco || null); } } 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); } } })(); res.json({ ok: true }); } catch (err) { console.error(err); res.status(500).json({ error: 'failed to save state' }); } }); // Partial state update (only changed parts) app.put('/api/state/partial', (req, res) => { try { const updates = req.body; if (!updates || typeof updates !== 'object') { return res.status(400).json({ error: 'Invalid update data' }); } const db = getDb(); // Use transaction for atomicity const result = db.transaction(() => { // Only update tables that were actually changed if (updates.pacientes) { db.prepare('DELETE FROM pacientes').run(); if (updates.pacientes.length > 0) { const stmt = db.prepare('INSERT INTO pacientes (id, apellido, nombre, dni, fechaNacimiento, sexo, telefono, email, direccion, obraSocial, nacionalidad, medicacionHabitual, antecedentes, alergias, grupoSanguineo, historiaClinica) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)'); for (const p of updates.pacientes) { stmt.run(p.id, p.apellido, p.nombre, p.dni, p.fechaNacimiento || null, p.sexo || null, p.telefono || null, p.email || null, p.direccion || null, p.obraSocial || null, p.nacionalidad || null, p.medicacionHabitual || null, p.antecedentes || null, p.alergias || null, p.grupoSanguineo || null, p.historiaClinica || null); } } } if (updates.areas) { db.prepare('DELETE FROM areas').run(); if (updates.areas.length > 0) { const stmt = db.prepare('INSERT INTO areas (id, nombre) VALUES (?, ?)'); for (const a of updates.areas) { stmt.run(a.id, a.nombre); } } } if (updates.camas) { db.prepare('DELETE FROM camas').run(); if (updates.camas.length > 0) { const stmt = db.prepare('INSERT INTO camas (id, numero, grupoId, areaId, tipo, estado, sector) VALUES (?, ?, ?, ?, ?, ?, ?)'); for (const c of updates.camas) { const sector = computeSector(c.numero); stmt.run(c.id, c.numero, c.grupoId || c.areaId || null, c.grupoId || c.areaId || null, c.tipo, c.estado, sector); } } } if (updates.internaciones) { db.prepare('DELETE FROM internaciones').run(); if (updates.internaciones.length > 0) { const stmt = db.prepare('INSERT OR IGNORE INTO internaciones (id, pacienteId, camaId, areaId, fechaIngresoHospital, fechaIngresoClinica, fechaEgreso, diagnosticoIngreso, motivoConsulta, enfermedadActual, antecedentesEnfermedadActual, diagnosticoEgreso, medicoIngresante, motivoEgreso, activa, apache, derivacion) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)'); for (const i of updates.internaciones) { stmt.run(i.id, i.pacienteId, i.camaId, i.areaId || null, i.fechaIngresoHospital || null, i.fechaIngresoClinica || null, i.fechaEgreso || null, i.diagnosticoIngreso || null, i.motivoConsulta || null, i.enfermedadActual || null, i.antecedentesEnfermedadActual || null, i.diagnosticoEgreso || null, i.medicoIngresante || null, i.motivoEgreso || null, i.activa ? 1 : 0, i.apache || null, i.derivacion || null); } } } if (updates.evoluciones) { db.prepare('DELETE FROM evoluciones').run(); if (updates.evoluciones.length > 0) { const stmt = db.prepare('INSERT OR IGNORE INTO evoluciones (id, internacionId, fecha, hora, medico, signosVitales, examenFisico, novedades, comentarios, pendientes) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)'); for (const e of updates.evoluciones) { stmt.run(e.id, e.internacionId, e.fecha, e.hora || '', e.medico || '', JSON.stringify(e.signosVitales || {}), JSON.stringify(e.examenFisico || {}), e.novedades || '', e.comentarios || '', e.pendientes || ''); } } } })(); res.json({ ok: true }); } catch (err) { console.error(err); res.status(500).json({ error: err.message }); } }); export default app; // === AUTENTICACIÓN === function generateUUID() { return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => { const r = (Math.random() * 16) | 0; const v = c === 'x' ? r : (r & 0x3) | 0x8; return v.toString(16); }); } app.post('/api/auth/login', (req, res) => { try { const { dni, password } = req.body; if (!dni || !password) { return res.status(400).json({ error: 'DNI y contraseña requeridos' }); } const usuario = getUsuarioByDni(dni); if (!usuario) { return res.status(401).json({ error: 'Usuario no encontrado' }); } const validPassword = verifyPassword(dni, password); if (!validPassword) { return res.status(401).json({ error: 'Contraseña incorrecta' }); } const { passwordHash, ...userWithoutPassword } = usuario; res.json(userWithoutPassword); } catch (err) { console.error(err); res.status(500).json({ error: 'Error en autenticación' }); } }); app.post('/api/auth/change-password', (req, res) => { try { const { dni, oldPassword, newPassword } = req.body; if (!dni || !oldPassword || !newPassword) { return res.status(400).json({ error: 'Todos los campos son requeridos' }); } const validPassword = verifyPassword(dni, oldPassword); if (!validPassword) { return res.status(401).json({ error: 'Contraseña actual incorrecta' }); } const newHash = hashPassword(newPassword); updateUsuarioByDni(dni, { passwordHash: newHash }); res.json({ ok: true }); } catch (err) { console.error(err); res.status(500).json({ error: 'Error al cambiar contraseña' }); } }); app.put('/api/auth/update-email', (req, res) => { try { const { dni, newEmail } = req.body; if (!dni || !newEmail) { return res.status(400).json({ error: 'DNI y email son requeridos' }); } updateUsuarioByDni(dni, { email: newEmail }); res.json({ ok: true }); } catch (err) { console.error(err); res.status(500).json({ error: 'Error al actualizar email' }); } }); // === GESTIÓN DE USUARIOS (ADMIN) === app.get('/api/grupos', (req, res) => { try { const areas = getAllGrupos(); res.json(areas); } catch (err) { console.error(err); res.status(500).json({ error: 'Error al obtener áreas' }); } }); app.get('/api/usuarios', (req, res) => { try { const usuarios = getAllUsuarios(); const usuariosSinPassword = usuarios.map(({ passwordHash, ...u }) => u); res.json(usuariosSinPassword); } catch (err) { console.error(err); res.status(500).json({ error: 'Error al obtener usuarios' }); } }); app.post('/api/usuarios', (req, res) => { try { const { apellido, nombre, dni, fechaNacimiento, email, rol, matriculaProfesional, password, areaId } = req.body; if (!apellido?.trim() || !nombre?.trim() || !dni?.trim() || !rol) { return res.status(400).json({ error: 'Apellido, nombre, DNI y rol son obligatorios' }); } const existente = getUsuarioByDni(dni.trim()); if (existente) { return res.status(400).json({ error: 'Ya existe un usuario con ese DNI' }); } const passwordFinal = (password && password.trim()) ? password.trim() : (dni.trim() || '123456'); const passwordHash = hashPassword(passwordFinal); const usuario = { id: generateUUID(), apellido: apellido.trim(), nombre: nombre.trim(), dni: dni.trim(), fechaNacimiento: fechaNacimiento?.trim() || null, email: email?.trim() || null, rol, matriculaProfesional: matriculaProfesional?.trim() || null, passwordHash, areaId: areaId || null, fechaCreacion: new Date().toISOString().split('T')[0] }; createUsuario(usuario); res.json({ ...usuario, passwordHash: undefined }); } catch (err) { console.error('Error al crear usuario:', err); res.status(500).json({ error: 'Error al crear usuario' }); } }); app.put('/api/usuarios/:id', (req, res) => { try { const { id } = req.params; const { apellido, nombre, dni, fechaNacimiento, email, rol, matriculaProfesional, areaId, password } = req.body; const usuario = getUsuarioById(id); if (!usuario) { return res.status(404).json({ error: 'Usuario no encontrado' }); } if (dni && dni.trim() !== usuario.dni) { const existente = getUsuarioByDni(dni.trim()); if (existente && existente.id !== id) { return res.status(400).json({ error: 'Ya existe otro usuario con ese DNI' }); } } const datos = {}; if (apellido !== undefined) datos.apellido = apellido.trim(); if (nombre !== undefined) datos.nombre = nombre.trim(); if (dni !== undefined) datos.dni = dni.trim(); if (fechaNacimiento !== undefined) datos.fechaNacimiento = fechaNacimiento?.trim() || null; if (email !== undefined) datos.email = email?.trim() || null; if (rol !== undefined) datos.rol = rol; if (matriculaProfesional !== undefined) datos.matriculaProfesional = matriculaProfesional?.trim() || null; if (areaId !== undefined) datos.areaId = areaId || null; if (password && password.trim()) { datos.passwordHash = hashPassword(password.trim()); } updateUsuario(id, datos); res.json({ ok: true }); } catch (err) { console.error('Error al actualizar usuario:', err); res.status(500).json({ error: 'Error al actualizar usuario' }); } }); app.delete('/api/usuarios/:id', (req, res) => { try { const { id } = req.params; deleteUsuario(id); res.json({ ok: true }); } catch (err) { console.error(err); res.status(500).json({ error: 'Error al eliminar usuario' }); } }); app.put('/api/camas/:id', (req, res) => { try { const { id } = req.params; updateCama(id, req.body); res.json({ ok: true }); } catch (err) { console.error('Error updating cama:', err.message); res.status(500).json({ error: err.message }); } }); // ========== PACIENTES ========== app.get('/api/pacientes', (req, res) => { try { res.json(getAllPacientes()); } catch (err) { res.status(500).json({ error: err.message }); } }); app.post('/api/pacientes', (req, res) => { try { const paciente = { ...req.body, id: req.body.id || generateUUID() }; createPaciente(paciente); res.json(paciente); } catch (err) { res.status(500).json({ error: err.message }); } }); app.put('/api/pacientes/:id', (req, res) => { try { updatePaciente(req.params.id, req.body); res.json({ ok: true }); } catch (err) { res.status(500).json({ error: err.message }); } }); app.delete('/api/pacientes/:id', (req, res) => { try { deletePaciente(req.params.id); res.json({ ok: true }); } catch (err) { res.status(500).json({ error: err.message }); } }); // ========== AREAS ========== app.post('/api/grupos', (req, res) => { try { const area = { ...req.body, id: req.body.id || generateUUID() }; createGrupo(area); res.json(area); } catch (err) { res.status(500).json({ error: err.message }); } }); app.put('/api/grupos/:id', (req, res) => { try { updateGrupo(req.params.id, req.body); res.json({ ok: true }); } catch (err) { res.status(500).json({ error: err.message }); } }); app.delete('/api/grupos/:id', (req, res) => { try { deleteGrupo(req.params.id); res.json({ ok: true }); } catch (err) { res.status(500).json({ error: err.message }); } }); // ========== CAMAS ========== app.get('/api/camas', (req, res) => { try { res.json(getAllCamas()); } catch (err) { res.status(500).json({ error: err.message }); } }); app.post('/api/camas', (req, res) => { try { const cama = { ...req.body, id: req.body.id || generateUUID() }; createCama(cama); res.json(cama); } catch (err) { res.status(500).json({ error: err.message }); } }); app.delete('/api/camas/:id', (req, res) => { try { deleteCama(req.params.id); res.json({ ok: true }); } catch (err) { res.status(500).json({ error: err.message }); } }); // ========== INTERNACIONES ========== app.get('/api/internaciones', (req, res) => { try { res.json(getAllInternaciones()); } catch (err) { res.status(500).json({ error: err.message }); } }); app.post('/api/internaciones', (req, res) => { try { const internacion = { ...req.body, id: req.body.id || generateUUID(), activa: true }; createInternacion(internacion); res.json(internacion); } catch (err) { res.status(500).json({ error: err.message }); } }); app.put('/api/internaciones/:id', (req, res) => { try { updateInternacion(req.params.id, req.body); res.json({ ok: true }); } catch (err) { res.status(500).json({ error: err.message }); } }); app.delete('/api/internaciones/:id', (req, res) => { try { deleteInternacion(req.params.id); res.json({ ok: true }); } catch (err) { res.status(500).json({ error: err.message }); } }); // ========== EVOLUCIONES ========== app.get('/api/evoluciones', (req, res) => { try { res.json(getAllEvoluciones()); } catch (err) { res.status(500).json({ error: err.message }); } }); app.post('/api/evoluciones', (req, res) => { try { const evolucion = { ...req.body, id: req.body.id || generateUUID() }; createEvolucion(evolucion); res.json(evolucion); } catch (err) { res.status(500).json({ error: err.message }); } }); app.put('/api/evoluciones/:id', (req, res) => { try { updateEvolucion(req.params.id, req.body); res.json({ ok: true }); } catch (err) { res.status(500).json({ error: err.message }); } }); app.delete('/api/evoluciones/:id', (req, res) => { try { deleteEvolucion(req.params.id); res.json({ ok: true }); } catch (err) { res.status(500).json({ error: err.message }); } }); // ========== LABORATORIOS ========== app.get('/api/laboratorios', (req, res) => { try { res.json(getAllLaboratorios()); } catch (err) { res.status(500).json({ error: err.message }); } }); app.post('/api/laboratorios', (req, res) => { try { const laboratorio = { ...req.body, id: req.body.id || generateUUID() }; createLaboratorio(laboratorio); res.json(laboratorio); } catch (err) { res.status(500).json({ error: err.message }); } }); app.put('/api/laboratorios/:id', (req, res) => { try { updateLaboratorio(req.params.id, req.body); res.json({ ok: true }); } catch (err) { res.status(500).json({ error: err.message }); } }); app.delete('/api/laboratorios/:id', (req, res) => { try { deleteLaboratorio(req.params.id); res.json({ ok: true }); } catch (err) { res.status(500).json({ error: err.message }); } }); // ========== GLUCEMIAS ========== app.get('/api/glucemias', (req, res) => { try { res.json(getAllGlucemias()); } catch (err) { res.status(500).json({ error: err.message }); } }); app.post('/api/glucemias', (req, res) => { try { const glucemia = { ...req.body, id: req.body.id || generateUUID() }; createGlucemia(glucemia); res.json(glucemia); } catch (err) { res.status(500).json({ error: err.message }); } }); app.put('/api/glucemias/:id', (req, res) => { try { updateGlucemia(req.params.id, req.body); res.json({ ok: true }); } catch (err) { res.status(500).json({ error: err.message }); } }); app.delete('/api/glucemias/:id', (req, res) => { try { deleteGlucemia(req.params.id); res.json({ ok: true }); } catch (err) { res.status(500).json({ error: err.message }); } }); // ========== ACIDOS BASE ========== app.get('/api/acid-os-base', (req, res) => { try { res.json(getAllAcidosBase()); } catch (err) { res.status(500).json({ error: err.message }); } }); app.post('/api/acid-os-base', (req, res) => { try { const acido = { ...req.body, id: req.body.id || generateUUID() }; createAcidoBase(acido); res.json(acido); } catch (err) { res.status(500).json({ error: err.message }); } }); app.put('/api/acid-os-base/:id', (req, res) => { try { updateAcidoBase(req.params.id, req.body); res.json({ ok: true }); } catch (err) { res.status(500).json({ error: err.message }); } }); app.delete('/api/acid-os-base/:id', (req, res) => { try { deleteAcidoBase(req.params.id); res.json({ ok: true }); } catch (err) { res.status(500).json({ error: err.message }); } }); // ========== CULTIVOS ========== app.get('/api/cultivos', (req, res) => { try { res.json(getAllCultivos()); } catch (err) { res.status(500).json({ error: err.message }); } }); app.post('/api/cultivos', (req, res) => { try { const cultivo = { ...req.body, id: req.body.id || generateUUID() }; createCultivo(cultivo); res.json(cultivo); } catch (err) { res.status(500).json({ error: err.message }); } }); app.put('/api/cultivos/:id', (req, res) => { try { updateCultivo(req.params.id, req.body); res.json({ ok: true }); } catch (err) { res.status(500).json({ error: err.message }); } }); app.delete('/api/cultivos/:id', (req, res) => { try { deleteCultivo(req.params.id); res.json({ ok: true }); } catch (err) { res.status(500).json({ error: err.message }); } }); // ========== ESTUDIOS COMPLEMENTARIOS ========== app.get('/api/estudios-complementarios', (req, res) => { try { res.json(getAllEstudiosComplementarios()); } catch (err) { res.status(500).json({ error: err.message }); } }); app.post('/api/estudios-complementarios', (req, res) => { try { const estudio = { ...req.body, id: req.body.id || generateUUID() }; createEstudioComplementario(estudio); res.json(estudio); } catch (err) { res.status(500).json({ error: err.message }); } }); app.put('/api/estudios-complementarios/:id', (req, res) => { try { updateEstudioComplementario(req.params.id, req.body); res.json({ ok: true }); } catch (err) { res.status(500).json({ error: err.message }); } }); app.delete('/api/estudios-complementarios/:id', (req, res) => { try { deleteEstudioComplementario(req.params.id); res.json({ ok: true }); } catch (err) { res.status(500).json({ error: err.message }); } }); // ========== INTERCONSULTAS ========== app.get('/api/interconsultas', (req, res) => { try { res.json(getAllInterconsultas()); } catch (err) { res.status(500).json({ error: err.message }); } }); app.post('/api/interconsultas', (req, res) => { try { const interconsulta = { ...req.body, id: req.body.id || generateUUID() }; createInterconsulta(interconsulta); res.json(interconsulta); } catch (err) { res.status(500).json({ error: err.message }); } }); app.put('/api/interconsultas/:id', (req, res) => { try { updateInterconsulta(req.params.id, req.body); res.json({ ok: true }); } catch (err) { res.status(500).json({ error: err.message }); } }); app.delete('/api/interconsultas/:id', (req, res) => { try { deleteInterconsulta(req.params.id); res.json({ ok: true }); } catch (err) { res.status(500).json({ error: err.message }); } }); // ========== ATB ========== app.get('/api/atb', (req, res) => { try { res.json(getAllAtb()); } catch (err) { res.status(500).json({ error: err.message }); } }); app.post('/api/atb', (req, res) => { try { const atb = { ...req.body, id: req.body.id || generateUUID() }; createAtb(atb); res.json(atb); } catch (err) { res.status(500).json({ error: err.message }); } }); app.put('/api/atb/:id', (req, res) => { try { updateAtb(req.params.id, req.body); res.json({ ok: true }); } catch (err) { res.status(500).json({ error: err.message }); } }); app.delete('/api/atb/:id', (req, res) => { try { deleteAtb(req.params.id); res.json({ ok: true }); } catch (err) { res.status(500).json({ error: err.message }); } }); // ========== INDICACIONES ========== app.get('/api/indicaciones', (req, res) => { try { res.json(getAllIndicaciones()); } catch (err) { res.status(500).json({ error: err.message }); } }); app.post('/api/indicaciones', (req, res) => { try { const indicacion = { ...req.body, id: req.body.id || generateUUID() }; createIndicacion(indicacion); res.json(indicacion); } catch (err) { res.status(500).json({ error: err.message }); } }); app.put('/api/indicaciones/:id', (req, res) => { try { updateIndicacion(req.params.id, req.body); res.json({ ok: true }); } catch (err) { res.status(500).json({ error: err.message }); } }); app.delete('/api/indicaciones/:id', (req, res) => { try { deleteIndicacion(req.params.id); res.json({ ok: true }); } catch (err) { res.status(500).json({ error: err.message }); } }); // ========== MOVIMIENTOS INDICACIONES ========== app.get('/api/movimientos-indicaciones', (req, res) => { try { res.json(getAllMovimientosIndicaciones()); } catch (err) { res.status(500).json({ error: err.message }); } }); app.post('/api/movimientos-indicaciones', (req, res) => { try { const movimiento = { ...req.body, id: req.body.id || generateUUID() }; createMovimientoIndicacion(movimiento); res.json(movimiento); } catch (err) { res.status(500).json({ error: err.message }); } }); // Helper para actualizar por DNI function updateUsuarioByDni(dni, datos) { const usuario = getUsuarioByDni(dni); if (usuario) { updateUsuario(usuario.id, datos); } }