Renombrar 'Areas' a 'Grupos' y actualizar dependencias

This commit is contained in:
2026-08-09 20:05:08 +00:00
parent 91cde0e5d9
commit dc108a9699
12 changed files with 421 additions and 342 deletions
+74 -11
View File
@@ -67,9 +67,15 @@ export function initDb() {
nombre TEXT NOT NULL UNIQUE
);
CREATE TABLE IF NOT EXISTS grupos (
id TEXT PRIMARY KEY,
nombre TEXT NOT NULL UNIQUE
);
CREATE TABLE IF NOT EXISTS camas (
id TEXT PRIMARY KEY,
numero TEXT NOT NULL,
grupoId TEXT,
areaId TEXT,
tipo TEXT DEFAULT 'Estándar',
estado TEXT DEFAULT 'Disponible',
@@ -81,6 +87,7 @@ export function initDb() {
id TEXT PRIMARY KEY,
pacienteId TEXT NOT NULL,
camaId TEXT,
grupoId TEXT,
areaId TEXT,
fechaIngresoHospital TEXT,
fechaIngresoClinica TEXT,
@@ -233,6 +240,36 @@ export function initDb() {
);
`);
// Migration helpers for columns if existing DB
try { db.exec('ALTER TABLE usuarios ADD COLUMN grupoId TEXT;'); } catch (_) {}
try { db.exec('ALTER TABLE camas ADD COLUMN grupoId TEXT;'); } catch (_) {}
try { db.exec('ALTER TABLE internaciones ADD COLUMN grupoId TEXT;'); } catch (_) {}
try { db.exec('ALTER TABLE usuarios ADD COLUMN areaId TEXT;'); } catch (_) {}
try { db.exec('ALTER TABLE camas ADD COLUMN areaId TEXT;'); } catch (_) {}
try { db.exec('ALTER TABLE internaciones ADD COLUMN areaId TEXT;'); } catch (_) {}
// Seed default 4 grupos if empty
try {
const defaultGrupos = [
{ id: 'patron-fondo', nombre: 'Patron Fondo' },
{ id: 'patron-adelante', nombre: 'Patron Adelante' },
{ id: 'segundo-acassuso', nombre: 'Segundo Acassuso' },
{ id: 'tercero-acassuso', nombre: 'Tercero Acassuso' }
];
const grupoCount = db.prepare('SELECT COUNT(*) as count FROM grupos').get();
if (!grupoCount || grupoCount.count === 0) {
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 defaultGrupos) {
stmtG.run(g.id, g.nombre);
stmtA.run(g.id, g.nombre);
}
}
} catch (err) {
console.error('Error seeding default grupos:', err);
}
// Create default admin if no users exist
try {
const userCount = db.prepare('SELECT COUNT(*) as count FROM usuarios').get();
@@ -351,11 +388,18 @@ export function getAllPacientes() {
return rows;
}
export function getAllAreas() {
const rows = db.prepare('SELECT * FROM areas').all();
export function getAllGrupos() {
const rows = db.prepare('SELECT * FROM grupos').all();
if (!rows || rows.length === 0) {
return db.prepare('SELECT * FROM areas').all();
}
return rows;
}
export function getAllAreas() {
return getAllGrupos();
}
export function getAllCamas() {
const rows = db.prepare('SELECT * FROM camas').all();
return rows;
@@ -467,24 +511,42 @@ export function deletePaciente(id) {
db.prepare('DELETE FROM pacientes WHERE id = ?').run(id);
}
// Areas
// Grupos & Areas
export function createGrupo(grupo) {
const stmtG = db.prepare('INSERT INTO grupos (id, nombre) VALUES (?, ?)');
const stmtA = db.prepare('INSERT INTO areas (id, nombre) VALUES (?, ?)');
stmtG.run(grupo.id, grupo.nombre);
try { stmtA.run(grupo.id, grupo.nombre); } catch (_) {}
}
export function updateGrupo(id, datos) {
if (datos.nombre) {
db.prepare('UPDATE grupos SET nombre = ? WHERE id = ?').run(datos.nombre, id);
try { db.prepare('UPDATE areas SET nombre = ? WHERE id = ?').run(datos.nombre, id); } catch (_) {}
}
}
export function deleteGrupo(id) {
db.prepare('DELETE FROM grupos WHERE id = ?').run(id);
try { db.prepare('DELETE FROM areas WHERE id = ?').run(id); } catch (_) {}
}
export function createArea(area) {
db.prepare('INSERT INTO areas (id, nombre) VALUES (?, ?)').run(area.id, area.nombre);
createGrupo(area);
}
export function updateArea(id, datos) {
if (datos.nombre) {
db.prepare('UPDATE areas SET nombre = ? WHERE id = ?').run(datos.nombre, id);
}
updateGrupo(id, datos);
}
export function deleteArea(id) {
db.prepare('DELETE FROM areas WHERE id = ?').run(id);
deleteGrupo(id);
}
// Camas
export function createCama(cama) {
db.prepare('INSERT INTO camas (id, numero, areaId, tipo, estado, pacienteId, internacionId) VALUES (?, ?, ?, ?, ?, ?, ?)').run(cama.id, cama.numero, cama.areaId || null, cama.tipo || 'Estándar', cama.estado || 'Disponible', cama.pacienteId || null, cama.internacionId || null);
const gId = cama.grupoId || cama.areaId || null;
db.prepare('INSERT INTO camas (id, numero, grupoId, areaId, tipo, estado, pacienteId, internacionId) VALUES (?, ?, ?, ?, ?, ?, ?, ?)').run(cama.id, cama.numero, gId, gId, cama.tipo || 'Estándar', cama.estado || 'Disponible', cama.pacienteId || null, cama.internacionId || null);
}
export function deleteCama(id) {
@@ -493,8 +555,9 @@ export function deleteCama(id) {
// Internaciones
export function createInternacion(i) {
const stmt = db.prepare('INSERT INTO internaciones (id, pacienteId, camaId, areaId, fechaIngresoHospital, fechaIngresoClinica, fechaEgreso, diagnosticoIngreso, motivoConsulta, enfermedadActual, antecedentesEnfermedadActual, diagnosticoEgreso, medicoIngresante, motivoEgreso, activa, apache, derivacion) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)');
stmt.run(i.id, i.pacienteId, i.camaId || null, 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);
const gId = i.grupoId || i.areaId || null;
const stmt = db.prepare('INSERT INTO internaciones (id, pacienteId, camaId, grupoId, areaId, fechaIngresoHospital, fechaIngresoClinica, fechaEgreso, diagnosticoIngreso, motivoConsulta, enfermedadActual, antecedentesEnfermedadActual, diagnosticoEgreso, medicoIngresante, motivoEgreso, activa, apache, derivacion) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)');
stmt.run(i.id, i.pacienteId, i.camaId || null, 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);
}
export function updateInternacion(id, datos) {
+26 -18
View File
@@ -3,7 +3,7 @@ import cors from 'cors';
import {
getDb, getValue, setValue, getUsuarioByDni, getUsuarioById, getAllUsuarios, createUsuario, updateUsuario, deleteUsuario, verifyPassword, hashPassword,
getAllPacientes, createPaciente, updatePaciente, deletePaciente,
getAllAreas, createArea, updateArea, deleteArea,
getAllGrupos, createGrupo, updateGrupo, deleteGrupo,
getAllCamas, createCama, updateCama, deleteCama,
getAllInternaciones, createInternacion, updateInternacion, deleteInternacion,
getAllEvoluciones, createEvolucion, updateEvolucion, deleteEvolucion,
@@ -27,9 +27,11 @@ const STORAGE_KEY = 'hospital-data-v1';
app.get('/api/state', (req, res) => {
try {
const gruposList = getAllGrupos();
const state = {
pacientes: getAllPacientes(),
areas: getAllAreas(),
grupos: gruposList,
areas: gruposList,
camas: getAllCamas(),
internaciones: getAllInternaciones(),
evoluciones: getAllEvoluciones(),
@@ -66,6 +68,7 @@ app.put('/api/state', (req, res) => {
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();
@@ -81,24 +84,29 @@ app.put('/api/state', (req, res) => {
}
}
if (state.areas?.length) {
const stmt = db.prepare('INSERT INTO areas (id, nombre) VALUES (?, ?)');
for (const a of state.areas) {
stmt.run(a.id, a.nombre);
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, areaId, tipo, estado) VALUES (?, ?, ?, ?, ?)');
const stmt = db.prepare('INSERT OR IGNORE INTO camas (id, numero, grupoId, areaId, tipo, estado) VALUES (?, ?, ?, ?, ?, ?)');
for (const c of state.camas) {
stmt.run(c.id, c.numero, c.areaId, c.tipo, c.estado);
const gId = c.grupoId || c.areaId || null;
stmt.run(c.id, c.numero, gId, gId, c.tipo, c.estado);
}
}
if (state.internaciones?.length) {
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 (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)');
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) {
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);
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);
}
}
@@ -326,9 +334,9 @@ app.put('/api/auth/update-email', (req, res) => {
// === GESTIÓN DE USUARIOS (ADMIN) ===
app.get('/api/areas', (req, res) => {
app.get('/api/grupos', (req, res) => {
try {
const areas = getAllAreas();
const areas = getAllGrupos();
res.json(areas);
} catch (err) {
console.error(err);
@@ -463,18 +471,18 @@ app.delete('/api/pacientes/:id', (req, res) => {
});
// ========== AREAS ==========
app.post('/api/areas', (req, res) => {
app.post('/api/grupos', (req, res) => {
try {
const area = { ...req.body, id: req.body.id || generateUUID() };
createArea(area);
createGrupo(area);
res.json(area);
} catch (err) { res.status(500).json({ error: err.message }); }
});
app.put('/api/areas/:id', (req, res) => {
try { updateArea(req.params.id, req.body); res.json({ ok: true }); } 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/areas/:id', (req, res) => {
try { deleteArea(req.params.id); 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 ==========
+9 -9
View File
@@ -49,7 +49,7 @@ function AppContent() {
return (
<MapaCamas
camas={store.camas}
areas={store.areas}
grupos={store.grupos}
pacientes={store.pacientes}
internaciones={store.internaciones}
onActualizarCama={store.actualizarCama}
@@ -58,9 +58,9 @@ function AppContent() {
onIniciarInternacion={store.iniciarInternacion}
getPacienteById={store.getPacienteById}
getInternacionById={store.getInternacionById}
onAgregarArea={store.agregarArea}
onActualizarArea={store.actualizarArea}
onEliminarArea={store.eliminarArea}
onAgregarGrupo={store.agregarGrupo}
onActualizarGrupo={store.actualizarGrupo}
onEliminarGrupo={store.eliminarGrupo}
/>
);
case 'pacientes':
@@ -79,7 +79,7 @@ function AppContent() {
internaciones={store.internaciones}
pacientes={store.pacientes}
camas={store.camas}
areas={store.areas}
grupos={store.grupos}
evoluciones={store.evoluciones}
laboratorios={store.laboratorios}
cultivos={store.cultivos}
@@ -207,11 +207,11 @@ function AppContent() {
<NuevoIngreso
pacientes={store.pacientes}
camas={store.camas}
areas={store.areas}
grupos={store.grupos}
onIniciarInternacion={store.iniciarInternacion}
onAgregarCama={store.agregarCama}
onVolver={() => store.setVista('internaciones')}
getAreaName={(areaId) => store.areas.find(a => a.id === areaId)?.nombre || ''}
getGrupoName={(grupoId) => store.grupos.find(a => a.id === grupoId)?.nombre || ''}
/>
);
@@ -234,11 +234,11 @@ function AppContent() {
cama={store.getCamaById(internacion.camaId)}
pacientes={store.pacientes}
camas={store.camas}
areas={store.areas}
grupos={store.grupos}
onActualizarInternacion={store.actualizarInternacion}
onAgregarCama={store.agregarCama}
onVolver={() => store.setVista('historiaclinica')}
getAreaName={(areaId) => store.areas.find(a => a.id === areaId)?.nombre || ''}
getGrupoName={(grupoId) => store.grupos.find(a => a.id === grupoId)?.nombre || ''}
/>
);
}
+161 -161
View File
@@ -1,10 +1,10 @@
import { useState, useEffect, useCallback } from 'react';
import { toast } from 'sonner';
import { getNombreProfesional, isCamaFueraDeArea } from '@/lib/utils';
import { getNombreProfesional, isCamaFueraDeGrupo } from '@/lib/utils';
import type {
Paciente,
Cama,
Area,
Grupo,
Internacion,
Evolucion,
Laboratorio,
@@ -36,7 +36,7 @@ function generateUUID(): string {
interface HospitalState {
pacientes: Paciente[];
camas: Cama[];
areas: Area[];
grupos: Grupo[];
internaciones: Internacion[];
evoluciones: Evolucion[];
laboratorios: Laboratorio[];
@@ -60,7 +60,7 @@ const API_BASE = import.meta.env.VITE_API_URL || '/api';
const defaultState = (): HospitalState => ({
pacientes: [],
areas: [],
grupos: [],
internaciones: [],
evoluciones: [],
laboratorios: [],
@@ -139,55 +139,55 @@ export function useHospitalStore() {
};
// Utility functions
const getCamaAreaId = useCallback((camaId: string): string | null => {
const getCamaGrupoId = useCallback((camaId: string): string | null => {
const cama = state.camas.find(c => c.id === camaId);
return cama?.areaId || null;
return cama?.grupoId || null;
}, [state.camas]);
const getInternacionAreaId = useCallback((internacionId: string): string | null => {
const getInternacionGrupoId = useCallback((internacionId: string): string | null => {
const internacion = state.internaciones.find(i => i.id === internacionId);
if (!internacion) return null;
return internacion.areaId || getCamaAreaId(internacion.camaId) || null;
}, [state.internaciones, getCamaAreaId]);
return internacion.grupoId || getCamaGrupoId(internacion.camaId) || null;
}, [state.internaciones, getCamaGrupoId]);
const getPacienteAreaId = useCallback((pacienteId: string): string | null => {
const getPacienteGrupoId = useCallback((pacienteId: string): string | null => {
const internacion = state.internaciones.find(i => i.pacienteId === pacienteId && i.activa);
if (!internacion) {
const anyInternacion = state.internaciones.find(i => i.pacienteId === pacienteId);
if (anyInternacion) return getCamaAreaId(anyInternacion.camaId);
if (anyInternacion) return getCamaGrupoId(anyInternacion.camaId);
return null;
}
return internacion.areaId || getCamaAreaId(internacion.camaId) || null;
}, [state.internaciones, getCamaAreaId]);
return internacion.grupoId || getCamaGrupoId(internacion.camaId) || null;
}, [state.internaciones, getCamaGrupoId]);
// Permission functions
const canAccessArea = useCallback((areaId: string | null | undefined): boolean => {
const canAccessGrupo = useCallback((grupoId: string | null | undefined): boolean => {
const user = state.currentUser;
if (!user) return false;
if (user.rol === 'admin') return true;
if (!areaId) return true;
if (!user.areaId) return true;
return user.areaId === areaId;
if (!grupoId) return true;
if (!user.grupoId) return true;
return user.grupoId === grupoId;
}, [state.currentUser]);
const checkAreaPermission = useCallback((areaId: string | null | undefined): boolean => {
const allowed = canAccessArea(areaId);
const checkGrupoPermission = useCallback((grupoId: string | null | undefined): boolean => {
const allowed = canAccessGrupo(grupoId);
if (!allowed) {
toast.error("no pertenecen al area de trabajo por la que se encuentra internado el paciente");
toast.error("no pertenecen al grupo de trabajo por la que se encuentra internado el paciente");
}
return allowed;
}, [canAccessArea]);
}, [canAccessGrupo]);
const canChangeBed = useCallback((internacionId: string): boolean => {
const internacion = state.internaciones.find(i => i.id === internacionId);
if (!internacion) return false;
return canAccessArea(internacion.areaId);
}, [state.internaciones, canAccessArea]);
return canAccessGrupo(internacion.grupoId);
}, [state.internaciones, canAccessGrupo]);
const canEditInternacion = useCallback((internacionId: string): boolean => {
const areaId = getInternacionAreaId(internacionId);
return canAccessArea(areaId);
}, [getInternacionAreaId, canAccessArea]);
const grupoId = getInternacionGrupoId(internacionId);
return canAccessGrupo(grupoId);
}, [getInternacionGrupoId, canAccessGrupo]);
const canEditIngreso = useCallback((internacionId: string): boolean => {
const internacion = state.internaciones.find(i => i.id === internacionId);
@@ -201,14 +201,14 @@ export function useHospitalStore() {
}, [state.internaciones, state.currentUser]);
const canEditCama = useCallback((camaId: string): boolean => {
const areaId = getCamaAreaId(camaId);
return canAccessArea(areaId);
}, [getCamaAreaId, canAccessArea]);
const grupoId = getCamaGrupoId(camaId);
return canAccessGrupo(grupoId);
}, [getCamaGrupoId, canAccessGrupo]);
const canEditPaciente = useCallback((pacienteId: string): boolean => {
const areaId = getPacienteAreaId(pacienteId);
return canAccessArea(areaId);
}, [getPacienteAreaId, canAccessArea]);
const grupoId = getPacienteGrupoId(pacienteId);
return canAccessGrupo(grupoId);
}, [getPacienteGrupoId, canAccessGrupo]);
const hasPermission = useCallback((permission: 'read' | 'write', section: string): boolean => {
const user = state.currentUser;
@@ -219,7 +219,7 @@ export function useHospitalStore() {
return permission === 'read';
}
if (user.rol === 'medico') {
if (section === 'miArea') return permission === 'read' || permission === 'write';
if (section === 'miGrupo') return permission === 'read' || permission === 'write';
return permission === 'read';
}
return false;
@@ -251,8 +251,8 @@ export function useHospitalStore() {
}, [apiCall]);
const actualizarPaciente = useCallback(async (id: string, datos: Partial<Paciente>) => {
const areaId = getPacienteAreaId(id);
if (!checkAreaPermission(areaId)) return;
const grupoId = getPacienteGrupoId(id);
if (!checkGrupoPermission(grupoId)) return;
try {
await apiCall('PUT', `/pacientes/${id}`, datos);
setState(prev => ({
@@ -263,11 +263,11 @@ export function useHospitalStore() {
console.error('Error al actualizar paciente:', err);
throw err;
}
}, [apiCall, checkAreaPermission, getPacienteAreaId]);
}, [apiCall, checkGrupoPermission, getPacienteGrupoId]);
const eliminarPaciente = useCallback(async (id: string) => {
const areaId = getPacienteAreaId(id);
if (!checkAreaPermission(areaId)) return;
const grupoId = getPacienteGrupoId(id);
if (!checkGrupoPermission(grupoId)) return;
try {
await apiCall('DELETE', `/pacientes/${id}`);
setState(prev => ({
@@ -278,15 +278,15 @@ export function useHospitalStore() {
console.error('Error al eliminar paciente:', err);
throw err;
}
}, [apiCall, checkAreaPermission, getPacienteAreaId]);
}, [apiCall, checkGrupoPermission, getPacienteGrupoId]);
// Acciones de camas
const actualizarCama = useCallback(async (id: string, datos: Partial<Cama>) => {
const cama = state.camas.find(c => c.id === id);
if (!cama) return;
const user = state.currentUser;
const areaId = cama.areaId || null;
if (user && user.rol !== 'admin' && areaId && user.areaId !== areaId) {
const grupoId = cama.grupoId || null;
if (user && user.rol !== 'admin' && grupoId && user.grupoId !== grupoId) {
console.warn('No tiene permisos para actualizar cama en esta área');
return;
}
@@ -300,7 +300,7 @@ export function useHospitalStore() {
console.error('Error al actualizar cama:', err);
throw err;
}
}, [state, canAccessArea, apiCall]);
}, [state, canAccessGrupo, apiCall]);
const agregarCama = useCallback(async (cama: Omit<Cama, 'id'>) => {
const nuevaCama: Cama = {
@@ -336,7 +336,7 @@ const agregarCama = useCallback(async (cama: Omit<Cama, 'id'>) => {
// Acciones de internaciones
const iniciarInternacion = useCallback(async (internacion: Omit<Internacion, 'id' | 'activa'>) => {
if (!checkAreaPermission(internacion.areaId)) {
if (!checkGrupoPermission(internacion.grupoId)) {
return null;
}
const nuevaInternacion: Internacion = {
@@ -380,9 +380,9 @@ const finalizarInternacion = useCallback(async (internacionId: string, datos: {
const internacion = state.internaciones.find(i => i.id === internacionId);
if (!internacion) return;
const areaFueraDeArea = state.areas.find(a => (a.nombre || '').toLowerCase().trim() === 'fuera de area');
const areaFueraDeAreaId = areaFueraDeArea?.id;
const esFueraDeArea = !internacion.areaId || !areaFueraDeAreaId || internacion.areaId === areaFueraDeAreaId;
const grupoFueraDeGrupo = state.grupos.find(a => (a.nombre || '').toLowerCase().trim() === 'fuera de grupo');
const grupoFueraDeGrupoId = grupoFueraDeGrupo?.id;
const esFueraDeGrupo = !internacion.grupoId || !grupoFueraDeGrupoId || internacion.grupoId === grupoFueraDeGrupoId;
const camaId = internacion.camaId;
try {
@@ -391,7 +391,7 @@ const finalizarInternacion = useCallback(async (internacionId: string, datos: {
// Eliminar o actualizar la cama según el área
if (camaId) {
if (esFueraDeArea) {
if (esFueraDeGrupo) {
// Eliminar la cama si es "Fuera de área"
await apiCall('DELETE', `/camas/${camaId}`);
} else {
@@ -411,7 +411,7 @@ const finalizarInternacion = useCallback(async (internacionId: string, datos: {
? { ...i, ...datos, activa: false }
: i
),
cams: esFueraDeArea
cams: esFueraDeGrupo
? prev.camas.filter(c => c.id !== camaId)
: prev.camas.map(c =>
c.id === camaId
@@ -449,11 +449,11 @@ const actualizarInternacion = useCallback(async (internacionId: string, datos: P
if (cambioDeCama && !canChangeBed(internacionId)) {
console.warn('No tiene permisos para cambiar la cama de esta internación');
checkAreaPermission(null); // triggers toast
checkGrupoPermission(null); // triggers toast
return;
}
if (!cambioDeCama && !checkAreaPermission(internacion.areaId)) {
if (!cambioDeCama && !checkGrupoPermission(internacion.grupoId)) {
console.warn('No tiene permisos para actualizar esta internación');
return;
}
@@ -497,13 +497,13 @@ const actualizarInternacion = useCallback(async (internacionId: string, datos: P
console.error('Error al actualizar internación:', err);
throw err;
}
}, [state, canAccessArea, checkAreaPermission, apiCall]);
}, [state, canAccessGrupo, checkGrupoPermission, apiCall]);
// Acciones de evoluciones
const agregarEvolucion = useCallback(async (evolucion: Omit<Evolucion, 'id'>) => {
const internacion = state.internaciones.find(i => i.id === evolucion.internacionId);
const areaId = internacion?.areaId || null;
if (!checkAreaPermission(areaId)) {
const grupoId = internacion?.grupoId || null;
if (!checkGrupoPermission(grupoId)) {
console.warn('No tiene permisos para agregar evolución en esta área');
return null;
}
@@ -523,14 +523,14 @@ const actualizarInternacion = useCallback(async (internacionId: string, datos: P
console.error('Error al agregar evolución:', err);
throw err;
}
}, [state, canAccessArea, checkAreaPermission, apiCall]);
}, [state, canAccessGrupo, checkGrupoPermission, apiCall]);
const eliminarEvolucion = useCallback(async (id: string) => {
const evolucion = state.evoluciones.find(e => e.id === id);
if (!evolucion) return;
const internacion = state.internaciones.find(i => i.id === evolucion.internacionId);
const areaId = internacion?.areaId || null;
if (!checkAreaPermission(areaId)) {
const grupoId = internacion?.grupoId || null;
if (!checkGrupoPermission(grupoId)) {
console.warn('No tiene permisos para eliminar evolución en esta área');
return;
}
@@ -545,14 +545,14 @@ const actualizarInternacion = useCallback(async (internacionId: string, datos: P
console.error('Error al eliminar evolución:', err);
throw err;
}
}, [state, canAccessArea, checkAreaPermission, apiCall]);
}, [state, canAccessGrupo, checkGrupoPermission, apiCall]);
const actualizarEvolucion = useCallback(async (id: string, datos: Partial<Evolucion>) => {
const evolucion = state.evoluciones.find(e => e.id === id);
if (!evolucion) return;
const internacion = state.internaciones.find(i => i.id === evolucion.internacionId);
const areaId = internacion?.areaId || null;
if (!checkAreaPermission(areaId)) {
const grupoId = internacion?.grupoId || null;
if (!checkGrupoPermission(grupoId)) {
console.warn('No tiene permisos para actualizar evolución en esta área');
return;
}
@@ -567,13 +567,13 @@ const actualizarInternacion = useCallback(async (internacionId: string, datos: P
console.error('Error al actualizar evolución:', err);
throw err;
}
}, [state, canAccessArea, checkAreaPermission, apiCall]);
}, [state, canAccessGrupo, checkGrupoPermission, apiCall]);
// Acciones de laboratorios
const agregarLaboratorio = useCallback(async (laboratorio: Omit<Laboratorio, 'id'>) => {
const internacion = state.internaciones.find(i => i.id === laboratorio.internacionId);
const areaId = internacion?.areaId || null;
if (!checkAreaPermission(areaId)) {
const grupoId = internacion?.grupoId || null;
if (!checkGrupoPermission(grupoId)) {
console.warn('No tiene permisos para agregar laboratorio en esta área');
return null;
}
@@ -592,14 +592,14 @@ const actualizarInternacion = useCallback(async (internacionId: string, datos: P
console.error('Error al agregar laboratorio:', err);
throw err;
}
}, [state, canAccessArea, checkAreaPermission, apiCall]);
}, [state, canAccessGrupo, checkGrupoPermission, apiCall]);
const eliminarLaboratorio = useCallback(async (id: string) => {
const laboratorio = state.laboratorios.find(l => l.id === id);
if (!laboratorio) return;
const internacion = state.internaciones.find(i => i.id === laboratorio.internacionId);
const areaId = internacion?.areaId || null;
if (!checkAreaPermission(areaId)) {
const grupoId = internacion?.grupoId || null;
if (!checkGrupoPermission(grupoId)) {
console.warn('No tiene permisos para eliminar laboratorio en esta área');
return;
}
@@ -613,14 +613,14 @@ const actualizarInternacion = useCallback(async (internacionId: string, datos: P
console.error('Error al eliminar laboratorio:', err);
throw err;
}
}, [state, canAccessArea, checkAreaPermission, apiCall]);
}, [state, canAccessGrupo, checkGrupoPermission, apiCall]);
const actualizarLaboratorio = useCallback(async (id: string, datos: Partial<Laboratorio>) => {
const laboratorio = state.laboratorios.find(l => l.id === id);
if (!laboratorio) return;
const internacion = state.internaciones.find(i => i.id === laboratorio.internacionId);
const areaId = internacion?.areaId || null;
if (!checkAreaPermission(areaId)) {
const grupoId = internacion?.grupoId || null;
if (!checkGrupoPermission(grupoId)) {
console.warn('No tiene permisos para actualizar laboratorio en esta área');
return;
}
@@ -634,13 +634,13 @@ const actualizarInternacion = useCallback(async (internacionId: string, datos: P
console.error('Error al actualizar laboratorio:', err);
throw err;
}
}, [state, canAccessArea, checkAreaPermission, apiCall]);
}, [state, canAccessGrupo, checkGrupoPermission, apiCall]);
// Acciones de glucemias
const agregarGlucemia = useCallback(async (glucemia: Omit<Glucemia, 'id'>) => {
const internacion = state.internaciones.find(i => i.id === glucemia.internacionId);
const areaId = internacion?.areaId || null;
if (!checkAreaPermission(areaId)) {
const grupoId = internacion?.grupoId || null;
if (!checkGrupoPermission(grupoId)) {
console.warn('No tiene permisos para agregar glucemia en esta área');
return null;
}
@@ -659,14 +659,14 @@ const actualizarInternacion = useCallback(async (internacionId: string, datos: P
console.error('Error al agregar glucemia:', err);
throw err;
}
}, [state, canAccessArea, checkAreaPermission, apiCall]);
}, [state, canAccessGrupo, checkGrupoPermission, apiCall]);
const eliminarGlucemia = useCallback(async (id: string) => {
const glucemia = state.glucemias.find(g => g.id === id);
if (!glucemia) return;
const internacion = state.internaciones.find(i => i.id === glucemia.internacionId);
const areaId = internacion?.areaId || null;
if (!checkAreaPermission(areaId)) {
const grupoId = internacion?.grupoId || null;
if (!checkGrupoPermission(grupoId)) {
console.warn('No tiene permisos para eliminar glucemia en esta área');
return;
}
@@ -680,14 +680,14 @@ const actualizarInternacion = useCallback(async (internacionId: string, datos: P
console.error('Error al eliminar glucemia:', err);
throw err;
}
}, [state, canAccessArea, checkAreaPermission, apiCall]);
}, [state, canAccessGrupo, checkGrupoPermission, apiCall]);
const actualizarGlucemia = useCallback(async (id: string, datos: Partial<Glucemia>) => {
const glucemia = state.glucemias.find(g => g.id === id);
if (!glucemia) return;
const internacion = state.internaciones.find(i => i.id === glucemia.internacionId);
const areaId = internacion?.areaId || null;
if (!checkAreaPermission(areaId)) {
const grupoId = internacion?.grupoId || null;
if (!checkGrupoPermission(grupoId)) {
console.warn('No tiene permisos para actualizar glucemia en esta área');
return;
}
@@ -701,13 +701,13 @@ const actualizarInternacion = useCallback(async (internacionId: string, datos: P
console.error('Error al actualizar glucemia:', err);
throw err;
}
}, [state, canAccessArea, checkAreaPermission, apiCall]);
}, [state, canAccessGrupo, checkGrupoPermission, apiCall]);
// Acciones de ácido-base
const agregarAcidoBase = useCallback(async (acidoBase: Omit<AcidoBase, 'id'>) => {
const internacion = state.internaciones.find(i => i.id === acidoBase.internacionId);
const areaId = internacion?.areaId || null;
if (!checkAreaPermission(areaId)) {
const grupoId = internacion?.grupoId || null;
if (!checkGrupoPermission(grupoId)) {
console.warn('No tiene permisos para agregar ácido-base en esta área');
return null;
}
@@ -726,14 +726,14 @@ const actualizarInternacion = useCallback(async (internacionId: string, datos: P
console.error('Error al agregar ácido-base:', err);
throw err;
}
}, [state, canAccessArea, checkAreaPermission, apiCall]);
}, [state, canAccessGrupo, checkGrupoPermission, apiCall]);
const actualizarAcidoBase = useCallback(async (id: string, datos: Partial<AcidoBase>) => {
const acidoBase = state.acidosBase.find(a => a.id === id);
if (!acidoBase) return;
const internacion = state.internaciones.find(i => i.id === acidoBase.internacionId);
const areaId = internacion?.areaId || null;
if (!checkAreaPermission(areaId)) {
const grupoId = internacion?.grupoId || null;
if (!checkGrupoPermission(grupoId)) {
console.warn('No tiene permisos para actualizar ácido-base en esta área');
return;
}
@@ -747,14 +747,14 @@ const actualizarInternacion = useCallback(async (internacionId: string, datos: P
console.error('Error al actualizar ácido-base:', err);
throw err;
}
}, [state, canAccessArea, checkAreaPermission, apiCall]);
}, [state, canAccessGrupo, checkGrupoPermission, apiCall]);
const eliminarAcidoBase = useCallback(async (id: string) => {
const acidoBase = state.acidosBase.find(a => a.id === id);
if (!acidoBase) return;
const internacion = state.internaciones.find(i => i.id === acidoBase.internacionId);
const areaId = internacion?.areaId || null;
if (!checkAreaPermission(areaId)) {
const grupoId = internacion?.grupoId || null;
if (!checkGrupoPermission(grupoId)) {
console.warn('No tiene permisos para eliminar ácido-base en esta área');
return;
}
@@ -768,13 +768,13 @@ const actualizarInternacion = useCallback(async (internacionId: string, datos: P
console.error('Error al eliminar ácido-base:', err);
throw err;
}
}, [state, canAccessArea, checkAreaPermission, apiCall]);
}, [state, canAccessGrupo, checkGrupoPermission, apiCall]);
// Acciones de cultivos
const agregarCultivo = useCallback(async (cultivo: Omit<Cultivo, 'id'>) => {
const internacion = state.internaciones.find(i => i.id === cultivo.internacionId);
const areaId = internacion?.areaId || null;
if (!checkAreaPermission(areaId)) {
const grupoId = internacion?.grupoId || null;
if (!checkGrupoPermission(grupoId)) {
console.warn('No tiene permisos para agregar cultivo en esta área');
return null;
}
@@ -793,14 +793,14 @@ const actualizarInternacion = useCallback(async (internacionId: string, datos: P
console.error('Error al agregar cultivo:', err);
throw err;
}
}, [state, canAccessArea, checkAreaPermission, apiCall]);
}, [state, canAccessGrupo, checkGrupoPermission, apiCall]);
const actualizarCultivo = useCallback(async (id: string, datos: Partial<Cultivo>) => {
const cultivo = state.cultivos.find(c => c.id === id);
if (!cultivo) return;
const internacion = state.internaciones.find(i => i.id === cultivo.internacionId);
const areaId = internacion?.areaId || null;
if (!checkAreaPermission(areaId)) {
const grupoId = internacion?.grupoId || null;
if (!checkGrupoPermission(grupoId)) {
console.warn('No tiene permisos para actualizar cultivo en esta área');
return;
}
@@ -814,14 +814,14 @@ const actualizarInternacion = useCallback(async (internacionId: string, datos: P
console.error('Error al actualizar cultivo:', err);
throw err;
}
}, [state, canAccessArea, checkAreaPermission, apiCall]);
}, [state, canAccessGrupo, checkGrupoPermission, apiCall]);
const eliminarCultivo = useCallback(async (id: string) => {
const cultivo = state.cultivos.find(c => c.id === id);
if (!cultivo) return;
const internacion = state.internaciones.find(i => i.id === cultivo.internacionId);
const areaId = internacion?.areaId || null;
if (!checkAreaPermission(areaId)) {
const grupoId = internacion?.grupoId || null;
if (!checkGrupoPermission(grupoId)) {
console.warn('No tiene permisos para eliminar cultivo en esta área');
return;
}
@@ -835,13 +835,13 @@ const actualizarInternacion = useCallback(async (internacionId: string, datos: P
console.error('Error al eliminar cultivo:', err);
throw err;
}
}, [state, canAccessArea, checkAreaPermission, apiCall]);
}, [state, canAccessGrupo, checkGrupoPermission, apiCall]);
// Acciones de estudios complementarios
const agregarEstudioComplementario = useCallback(async (estudio: Omit<EstudioComplementario, 'id'>) => {
const internacion = state.internaciones.find(i => i.id === estudio.internacionId);
const areaId = internacion?.areaId || null;
if (!checkAreaPermission(areaId)) {
const grupoId = internacion?.grupoId || null;
if (!checkGrupoPermission(grupoId)) {
console.warn('No tiene permisos para agregar estudio complementario en esta área');
return null;
}
@@ -860,14 +860,14 @@ const actualizarInternacion = useCallback(async (internacionId: string, datos: P
console.error('Error al agregar estudio:', err);
throw err;
}
}, [state, canAccessArea, checkAreaPermission, apiCall]);
}, [state, canAccessGrupo, checkGrupoPermission, apiCall]);
const actualizarEstudioComplementario = useCallback(async (id: string, datos: Partial<EstudioComplementario>) => {
const estudio = state.estudiosComplementarios.find(e => e.id === id);
if (!estudio) return;
const internacion = state.internaciones.find(i => i.id === estudio.internacionId);
const areaId = internacion?.areaId || null;
if (!checkAreaPermission(areaId)) {
const grupoId = internacion?.grupoId || null;
if (!checkGrupoPermission(grupoId)) {
console.warn('No tiene permisos para actualizar estudio complementario en esta área');
return;
}
@@ -881,14 +881,14 @@ const actualizarInternacion = useCallback(async (internacionId: string, datos: P
console.error('Error al actualizar estudio:', err);
throw err;
}
}, [state, canAccessArea, checkAreaPermission, apiCall]);
}, [state, canAccessGrupo, checkGrupoPermission, apiCall]);
const eliminarEstudioComplementario = useCallback(async (id: string) => {
const estudio = state.estudiosComplementarios.find(e => e.id === id);
if (!estudio) return;
const internacion = state.internaciones.find(i => i.id === estudio.internacionId);
const areaId = internacion?.areaId || null;
if (!checkAreaPermission(areaId)) {
const grupoId = internacion?.grupoId || null;
if (!checkGrupoPermission(grupoId)) {
console.warn('No tiene permisos para eliminar estudio complementario en esta área');
return;
}
@@ -902,13 +902,13 @@ const actualizarInternacion = useCallback(async (internacionId: string, datos: P
console.error('Error al eliminar estudio:', err);
throw err;
}
}, [state, canAccessArea, checkAreaPermission, apiCall]);
}, [state, canAccessGrupo, checkGrupoPermission, apiCall]);
// Acciones de interconsultas
const agregarInterconsulta = useCallback(async (interconsulta: Omit<Interconsulta, 'id'>) => {
const internacion = state.internaciones.find(i => i.id === interconsulta.internacionId);
const areaId = internacion?.areaId || null;
if (!checkAreaPermission(areaId)) {
const grupoId = internacion?.grupoId || null;
if (!checkGrupoPermission(grupoId)) {
console.warn('No tiene permisos para agregar interconsulta en esta área');
return null;
}
@@ -924,14 +924,14 @@ const actualizarInternacion = useCallback(async (internacionId: string, datos: P
console.error('Error al agregar interconsulta:', err);
throw err;
}
}, [state, canAccessArea, checkAreaPermission, apiCall]);
}, [state, canAccessGrupo, checkGrupoPermission, apiCall]);
const actualizarInterconsulta = useCallback(async (id: string, datos: Partial<Interconsulta>) => {
const interconsulta = (state.interconsultas || []).find(ic => ic.id === id);
if (!interconsulta) return;
const internacion = state.internaciones.find(i => i.id === interconsulta.internacionId);
const areaId = internacion?.areaId || null;
if (!checkAreaPermission(areaId)) {
const grupoId = internacion?.grupoId || null;
if (!checkGrupoPermission(grupoId)) {
console.warn('No tiene permisos para actualizar interconsulta en esta área');
return;
}
@@ -945,14 +945,14 @@ const actualizarInternacion = useCallback(async (internacionId: string, datos: P
console.error('Error al actualizar interconsulta:', err);
throw err;
}
}, [state, canAccessArea, checkAreaPermission, apiCall]);
}, [state, canAccessGrupo, checkGrupoPermission, apiCall]);
const eliminarInterconsulta = useCallback(async (id: string) => {
const interconsulta = (state.interconsultas || []).find(ic => ic.id === id);
if (!interconsulta) return;
const internacion = state.internaciones.find(i => i.id === interconsulta.internacionId);
const areaId = internacion?.areaId || null;
if (!checkAreaPermission(areaId)) {
const grupoId = internacion?.grupoId || null;
if (!checkGrupoPermission(grupoId)) {
console.warn('No tiene permisos para eliminar interconsulta en esta área');
return;
}
@@ -966,12 +966,12 @@ const actualizarInternacion = useCallback(async (internacionId: string, datos: P
console.error('Error al eliminar interconsulta:', err);
throw err;
}
}, [state, canAccessArea, checkAreaPermission, apiCall]);
}, [state, canAccessGrupo, checkGrupoPermission, apiCall]);
const agregarATB = useCallback(async (atb: Omit<ATB, 'id'>) => {
const internacion = state.internaciones.find(i => i.id === atb.internacionId);
const areaId = internacion?.areaId || null;
if (!checkAreaPermission(areaId)) {
const grupoId = internacion?.grupoId || null;
if (!checkGrupoPermission(grupoId)) {
console.warn('No tiene permisos para agregar ATB en esta área');
return null;
}
@@ -987,14 +987,14 @@ const actualizarInternacion = useCallback(async (internacionId: string, datos: P
console.error('Error al agregar ATB:', err);
throw err;
}
}, [state, canAccessArea, checkAreaPermission, apiCall]);
}, [state, canAccessGrupo, checkGrupoPermission, apiCall]);
const actualizarATB = useCallback(async (id: string, datos: Partial<ATB>) => {
const atb = state.atb.find(a => a.id === id);
if (!atb) return;
const internacion = state.internaciones.find(i => i.id === atb.internacionId);
const areaId = internacion?.areaId || null;
if (!checkAreaPermission(areaId)) {
const grupoId = internacion?.grupoId || null;
if (!checkGrupoPermission(grupoId)) {
console.warn('No tiene permisos para actualizar ATB en esta área');
return;
}
@@ -1008,14 +1008,14 @@ const actualizarInternacion = useCallback(async (internacionId: string, datos: P
console.error('Error al actualizar ATB:', err);
throw err;
}
}, [state, canAccessArea, checkAreaPermission, apiCall]);
}, [state, canAccessGrupo, checkGrupoPermission, apiCall]);
const eliminarATB = useCallback(async (id: string) => {
const atb = state.atb.find(a => a.id === id);
if (!atb) return;
const internacion = state.internaciones.find(i => i.id === atb.internacionId);
const areaId = internacion?.areaId || null;
if (!checkAreaPermission(areaId)) {
const grupoId = internacion?.grupoId || null;
if (!checkGrupoPermission(grupoId)) {
console.warn('No tiene permisos para eliminar ATB en esta área');
return;
}
@@ -1029,12 +1029,12 @@ const actualizarInternacion = useCallback(async (internacionId: string, datos: P
console.error('Error al eliminar ATB:', err);
throw err;
}
}, [state, canAccessArea, checkAreaPermission, apiCall]);
}, [state, canAccessGrupo, checkGrupoPermission, apiCall]);
const agregarIndicacion = useCallback(async (indicacion: Omit<Indicacion, 'id'>) => {
const internacion = state.internaciones.find(i => i.id === indicacion.internacionId);
const areaId = internacion?.areaId || null;
if (!checkAreaPermission(areaId)) {
const grupoId = internacion?.grupoId || null;
if (!checkGrupoPermission(grupoId)) {
console.warn('No tiene permisos para agregar indicación en esta área');
return null;
}
@@ -1050,14 +1050,14 @@ const actualizarInternacion = useCallback(async (internacionId: string, datos: P
console.error('Error al agregar indicación:', err);
throw err;
}
}, [state.internaciones, canAccessArea, checkAreaPermission, apiCall]);
}, [state.internaciones, canAccessGrupo, checkGrupoPermission, apiCall]);
const actualizarIndicacion = useCallback(async (id: string, datos: Partial<Indicacion>) => {
const indicacion = state.indicaciones.find(i => i.id === id);
if (!indicacion) return;
const internacion = state.internaciones.find(i => i.id === indicacion.internacionId);
const areaId = internacion?.areaId || null;
if (!checkAreaPermission(areaId)) {
const grupoId = internacion?.grupoId || null;
if (!checkGrupoPermission(grupoId)) {
console.warn('No tiene permisos para actualizar indicación en esta área');
return;
}
@@ -1071,14 +1071,14 @@ const actualizarInternacion = useCallback(async (internacionId: string, datos: P
console.error('Error al actualizar indicación:', err);
throw err;
}
}, [state.indicaciones, state.internaciones, canAccessArea, checkAreaPermission, apiCall]);
}, [state.indicaciones, state.internaciones, canAccessGrupo, checkGrupoPermission, apiCall]);
const eliminarIndicacion = useCallback(async (id: string) => {
const indicacion = state.indicaciones.find(i => i.id === id);
if (!indicacion) return;
const internacion = state.internaciones.find(i => i.id === indicacion.internacionId);
const areaId = internacion?.areaId || null;
if (!checkAreaPermission(areaId)) {
const grupoId = internacion?.grupoId || null;
if (!checkGrupoPermission(grupoId)) {
console.warn('No tiene permisos para eliminar indicación en esta área');
return;
}
@@ -1092,12 +1092,12 @@ const actualizarInternacion = useCallback(async (internacionId: string, datos: P
console.error('Error al eliminar indicación:', err);
throw err;
}
}, [state.indicaciones, state.internaciones, canAccessArea, checkAreaPermission, apiCall]);
}, [state.indicaciones, state.internaciones, canAccessGrupo, checkGrupoPermission, apiCall]);
const agregarMovimientoIndicacion = useCallback(async (movimiento: any) => {
const internacion = state.internaciones.find(i => i.id === movimiento.internacionId);
const areaId = internacion?.areaId || null;
if (!checkAreaPermission(areaId)) {
const grupoId = internacion?.grupoId || null;
if (!checkGrupoPermission(grupoId)) {
console.warn('No tiene permisos para agregar movimiento de indicación en esta área');
return;
}
@@ -1112,7 +1112,7 @@ const actualizarInternacion = useCallback(async (internacionId: string, datos: P
console.error('Error al agregar movimiento:', err);
throw err;
}
}, [state, canAccessArea, checkAreaPermission, apiCall]);
}, [state, canAccessGrupo, checkGrupoPermission, apiCall]);
const getMovimientosByInternacion = useCallback((internacionId: string) => {
return (state.movimientosIndicaciones || [])
@@ -1120,28 +1120,28 @@ const actualizarInternacion = useCallback(async (internacionId: string, datos: P
.sort((a: any, b: any) => new Date(b.fecha).getTime() - new Date(a.fecha).getTime());
}, [state.movimientosIndicaciones]);
// Acciones de areas
const agregarArea = useCallback(async (area: Omit<Area, 'id'>) => {
const nuevaArea: Area = { ...area, id: generateUUID() };
// Acciones de grupos
const agregarGrupo = useCallback(async (grupo: Omit<Grupo, 'id'>) => {
const nuevaGrupo: Grupo = { ...grupo, id: generateUUID() };
try {
await apiCall('POST', '/areas', nuevaArea);
await apiCall('POST', '/grupos', nuevaGrupo);
setState(prev => ({
...prev,
areas: [...prev.areas, nuevaArea],
grupos: [...prev.grupos, nuevaGrupo],
}));
return nuevaArea.id;
return nuevaGrupo.id;
} catch (err) {
console.error('Error al agregar área:', err);
throw err;
}
}, [apiCall]);
const actualizarArea = useCallback(async (id: string, datos: Partial<Area>) => {
const actualizarGrupo = useCallback(async (id: string, datos: Partial<Grupo>) => {
try {
await apiCall('PUT', `/areas/${id}`, datos);
await apiCall('PUT', `/grupos/${id}`, datos);
setState(prev => ({
...prev,
areas: prev.areas.map(a => a.id === id ? { ...a, ...datos } : a),
grupos: prev.grupos.map(a => a.id === id ? { ...a, ...datos } : a),
}));
} catch (err) {
console.error('Error al actualizar área:', err);
@@ -1149,13 +1149,13 @@ const actualizarInternacion = useCallback(async (internacionId: string, datos: P
}
}, [apiCall]);
const eliminarArea = useCallback(async (id: string) => {
const eliminarGrupo = useCallback(async (id: string) => {
try {
await apiCall('DELETE', `/areas/${id}`);
await apiCall('DELETE', `/grupos/${id}`);
setState(prev => ({
...prev,
areas: prev.areas.filter(a => a.id !== id),
camas: prev.camas.map(c => c.areaId === id ? { ...c, areaId: undefined } : c),
grupos: prev.grupos.filter(a => a.id !== id),
camas: prev.camas.map(c => c.grupoId === id ? { ...c, grupoId: undefined } : c),
}));
} catch (err) {
console.error('Error al eliminar área:', err);
@@ -1211,11 +1211,11 @@ const actualizarInternacion = useCallback(async (internacionId: string, datos: P
}, [state.cultivos]);
const getEstadisticas = useCallback(() => {
const fueraDeAreaList = state.camas.filter(c => isCamaFueraDeArea(c, state.areas));
const camasActivas = state.camas.filter(c => !isCamaFueraDeArea(c, state.areas));
const camaPrincipal = state.camas.filter(c => !isCamaFueraDeArea(c, state.areas));
const camaOcupadas = state.camas.filter(c => c.estado === 'Ocupada' && !isCamaFueraDeArea(c, state.areas)).length;
const camasDisponibles = state.camas.filter(c => c.estado === 'Disponible' && !isCamaFueraDeArea(c, state.areas)).length;
const fueraDeGrupoList = state.camas.filter(c => isCamaFueraDeGrupo(c, state.grupos));
const camasActivas = state.camas.filter(c => !isCamaFueraDeGrupo(c, state.grupos));
const camaPrincipal = state.camas.filter(c => !isCamaFueraDeGrupo(c, state.grupos));
const camaOcupadas = state.camas.filter(c => c.estado === 'Ocupada' && !isCamaFueraDeGrupo(c, state.grupos)).length;
const camasDisponibles = state.camas.filter(c => c.estado === 'Disponible' && !isCamaFueraDeGrupo(c, state.grupos)).length;
const camasMantenimiento = state.camas.filter(c => c.estado === 'Reparacion').length;
const internacionesActivas = state.internaciones.filter(i => i.activa).length;
const totalPacientes = state.pacientes.length;
@@ -1228,7 +1228,7 @@ const actualizarInternacion = useCallback(async (internacionId: string, datos: P
camasMantenimiento,
totalCamas: state.camas.length,
totalCamasActivas: camasActivas.length,
camasFueraDeArea: fueraDeAreaList.length,
camasFueraDeGrupo: fueraDeGrupoList.length,
internacionesActivas,
totalPacientes,
cultivosPendientes,
@@ -1338,9 +1338,9 @@ const actualizarInternacion = useCallback(async (internacionId: string, datos: P
eliminarIndicacion,
agregarMovimientoIndicacion,
getMovimientosByInternacion,
agregarArea,
actualizarArea,
eliminarArea,
agregarGrupo,
actualizarGrupo,
eliminarGrupo,
getPacienteById,
getCamaById,
getInternacionById,
@@ -1357,14 +1357,14 @@ const actualizarInternacion = useCallback(async (internacionId: string, datos: P
changePassword,
updateEmail,
hasPermission,
canAccessArea,
canAccessGrupo,
canChangeBed,
canEditInternacion,
canEditIngreso,
canEditCama,
canEditPaciente,
getCamaAreaId,
getInternacionAreaId,
getPacienteAreaId,
getCamaGrupoId,
getInternacionGrupoId,
getPacienteGrupoId,
};
}
+12 -9
View File
@@ -38,20 +38,23 @@ export function calcularEdad(fechaNacimiento?: string): string | number {
return edad;
}
export function isCamaFueraDeArea(
cama: { numero: string; areaId?: string },
areas?: { id: string; nombre: string }[]
export function isCamaFueraDeGrupo(
cama: { numero: string; grupoId?: string; areaId?: string },
gruposOrAreas?: { id: string; nombre: string }[]
): boolean {
if (cama.areaId === 'Fuera de Area') {
if (cama.grupoId === 'Fuera de Area' || cama.areaId === 'Fuera de Area') {
return true;
}
if (areas && cama.areaId) {
const area = areas.find(a => (a.nombre || '').toLowerCase().trim() === 'fuera de area');
if (area && cama.areaId === area.id) {
if (gruposOrAreas) {
const targetId = cama.grupoId || cama.areaId;
if (targetId) {
const g = gruposOrAreas.find(a => (a.nombre || '').toLowerCase().trim() === 'fuera de area');
if (g && targetId === g.id) {
return true;
}
}
}
const numero = cama.numero;
if (!numero) return false;
@@ -61,12 +64,12 @@ export function isCamaFueraDeArea(
const salaNum = parseInt(salaStr, 10);
if (isNaN(salaNum)) return false;
// Formato 3XX que sean impares
// Formato 3XX que sean impares (301, 303, 305, etc.)
if (salaNum >= 300 && salaNum < 400 && salaNum % 2 !== 0) {
return true;
}
// Todas las 4XX (sean pares o impares)
// Formato 4XX (todas las 4XX, pares o impares)
if (salaNum >= 400 && salaNum < 500) {
return true;
}
+1 -1
View File
@@ -95,7 +95,7 @@ export function Dashboard({
<span className="text-3xl font-bold">{estadisticas.camasFueraDeArea}</span>
</div>
<p className="text-xs text-gray-400 dark:text-gray-400 mt-1">
Camas Fuera de Area
Camas Fuera de Grupo
</p>
</CardContent>
</Card>
+17 -17
View File
@@ -7,7 +7,7 @@ import { Label } from '@/components/ui/label';
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
import { Badge } from '@/components/ui/badge';
import { toast } from 'sonner';
import type { Paciente, Cama, Area, Internacion } from '@/types';
import type { Paciente, Cama, Grupo, Internacion } from '@/types';
import { getNombreProfesional, calcularEdad } from '@/lib/utils';
import { useHospitalStore } from '@/hooks/useHospitalStore';
@@ -17,18 +17,18 @@ interface EditIngresoProps {
cama?: Cama;
pacientes: Paciente[];
camas: Cama[];
areas: Area[];
grupos: Grupo[];
onActualizarInternacion: (id: string, datos: Partial<Internacion>) => Promise<void> | void;
onAgregarCama?: (cama: Omit<Cama, 'id'>) => Promise<string>;
onVolver: () => void;
getAreaName: (areaId: string | undefined) => string;
getGrupoName: (grupoId: string | undefined) => string;
}
export function EditIngreso({
internacion,
pacientes,
camas,
areas,
grupos,
onActualizarInternacion,
onAgregarCama,
onVolver,
@@ -36,7 +36,7 @@ export function EditIngreso({
const { currentUser } = useHospitalStore();
const [pacienteSeleccionado] = useState(internacion.pacienteId);
const [camaSeleccionada] = useState(internacion.camaId);
const [areaSeleccionada] = useState(internacion.areaId);
const [grupoSeleccionada] = useState(internacion.grupoId);
const [camaInput, setCamaInput] = useState('');
const [modoCama, setModoCama] = useState<'seleccionar' | 'escribir'>('seleccionar');
const effectiveMedico = getNombreProfesional(currentUser);
@@ -52,8 +52,8 @@ export function EditIngreso({
const selectedPaciente = pacientes.find(p => p.id === pacienteSeleccionado);
const normalizeStr = (str?: string) => (str || '').normalize('NFD').replace(/[\u0300-\u036f]/g, '').trim().toLowerCase();
const areaFueraDeArea = areas.find(a => normalizeStr(a.nombre) === 'fuera de area');
const areaFueraDeAreaId = areaFueraDeArea?.id || areas[0]?.id || 'fuera-de-area';
const grupoFueraDeGrupo = grupos.find(a => normalizeStr(a.nombre) === 'fuera de grupo');
const grupoFueraDeGrupoId = grupoFueraDeGrupo?.id || grupos[0]?.id || 'fuera-de-grupo';
const parseBedNumber = (numero: string) => {
const match = numero.match(/^(\d{3})-(\d+)$/);
@@ -123,7 +123,7 @@ export function EditIngreso({
try {
let camaId = '';
let newAreaId = '';
let newGrupoId = '';
if (modoCama === 'escribir') {
if (!camaInput.trim()) {
@@ -135,15 +135,15 @@ export function EditIngreso({
const camaExistente = camas.find(c => c.numero === numeroCama);
if (camaExistente) {
camaId = camaExistente.id;
newAreaId = camaExistente.areaId || areaFueraDeAreaId;
newGrupoId = camaExistente.grupoId || grupoFueraDeGrupoId;
} else if (onAgregarCama) {
camaId = await onAgregarCama({
numero: numeroCama,
areaId: areaFueraDeAreaId,
grupoId: grupoFueraDeGrupoId,
tipo: 'General',
estado: 'Ocupada'
});
newAreaId = areaFueraDeAreaId;
newGrupoId = grupoFueraDeGrupoId;
} else {
toast.error('No se puede crear la cama');
setIsSubmitting(false);
@@ -157,10 +157,10 @@ export function EditIngreso({
setIsSubmitting(false);
return;
}
newAreaId = camaElegida.areaId || areaFueraDeAreaId;
newGrupoId = camaElegida.grupoId || grupoFueraDeGrupoId;
camaId = camaSeleccionada;
} else {
newAreaId = areaSeleccionada || internacion.areaId || areaFueraDeAreaId;
newGrupoId = grupoSeleccionada || internacion.grupoId || grupoFueraDeGrupoId;
camaId = internacion.camaId;
}
}
@@ -168,7 +168,7 @@ export function EditIngreso({
await onActualizarInternacion(internacion.id, {
pacienteId: pacienteSeleccionado,
camaId: camaId,
areaId: newAreaId,
grupoId: newGrupoId,
medicoIngresante: effectiveMedico.trim(),
diagnosticoIngreso: diagnosticoIngreso.trim(),
motivoConsulta: motivoConsulta.trim() || undefined,
@@ -290,13 +290,13 @@ export function EditIngreso({
</Card>
</div>
{/* Columna 2: Cama y Área */}
{/* Columna 2: Cama y Grupo */}
<div className="space-y-6">
<Card className="min-h-[200px]">
<CardContent className="pt-6 space-y-4">
<h3 className="font-semibold text-gray-900 dark:text-white flex items-center gap-2">
<Bed className="h-4 w-4" />
Asignación de Cama y Área
Asignación de Cama y Grupo
</h3>
<div className="flex gap-2 mb-4">
@@ -325,7 +325,7 @@ export function EditIngreso({
<SelectContent>
{sortedCamas.map(c => (
<SelectItem key={c.id} value={c.id}>
{c.numero} - {getAreaName(c.areaId)}
{c.numero} - {getGrupoName(c.grupoId)}
</SelectItem>
))}
</SelectContent>
+20 -20
View File
@@ -1,6 +1,6 @@
import { useState, useEffect } from 'react';
import { useHospitalStore } from '@/hooks/useHospitalStore';
import type { Usuario, RolUsuario, Area } from '@/types';
import type { Usuario, RolUsuario, Grupo } from '@/types';
import { Card, CardContent } from '@/components/ui/card';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
@@ -16,7 +16,7 @@ const API_BASE = import.meta.env.VITE_API_URL || '/api';
export function GestionUsuarios() {
const { currentUser } = useHospitalStore();
const [usuarios, setUsuarios] = useState<Usuario[]>([]);
const [areas, setAreas] = useState<Area[]>([]);
const [grupos, setGrupos] = useState<Grupo[]>([]);
const [loading, setLoading] = useState(true);
const [dialogOpen, setDialogOpen] = useState(false);
const [editUsuario, setEditUsuario] = useState<Usuario | null>(null);
@@ -30,12 +30,12 @@ export function GestionUsuarios() {
rol: 'medico' as RolUsuario,
matriculaProfesional: '',
password: '',
areaId: '',
grupoId: '',
});
useEffect(() => {
fetchUsuarios();
fetchAreas();
fetchGrupos();
}, []);
const fetchUsuarios = async () => {
@@ -50,11 +50,11 @@ export function GestionUsuarios() {
}
};
const fetchAreas = async () => {
const fetchGrupos = async () => {
try {
const res = await fetch(`${API_BASE}/areas`);
const res = await fetch(`${API_BASE}/grupos`);
const data = await res.json();
setAreas(data);
setGrupos(data);
} catch (err) {
console.error(err);
}
@@ -70,7 +70,7 @@ export function GestionUsuarios() {
rol: 'medico',
matriculaProfesional: '',
password: '',
areaId: '',
grupoId: '',
});
setEditUsuario(null);
};
@@ -86,7 +86,7 @@ export function GestionUsuarios() {
rol: usu.rol,
matriculaProfesional: usu.matriculaProfesional || '',
password: '',
areaId: usu.areaId || '',
grupoId: usu.grupoId || '',
});
setDialogOpen(true);
};
@@ -152,9 +152,9 @@ export function GestionUsuarios() {
}
};
const getAreaName = (areaId?: string) => {
if (!areaId) return '-';
return areas.find(a => a.id === areaId)?.nombre || '-';
const getGrupoName = (grupoId?: string) => {
if (!grupoId) return '-';
return grupos.find(a => a.id === grupoId)?.nombre || '-';
};
if (!currentUser || currentUser.rol !== 'admin') {
@@ -191,7 +191,7 @@ export function GestionUsuarios() {
<TableHead>Apellido, Nombre</TableHead>
<TableHead>DNI</TableHead>
<TableHead>Rol</TableHead>
<TableHead>Área</TableHead>
<TableHead>Grupo</TableHead>
<TableHead>Email</TableHead>
<TableHead>Matrícula</TableHead>
<TableHead>Acciones</TableHead>
@@ -207,7 +207,7 @@ export function GestionUsuarios() {
{getRolLabel(usu.rol)}
</Badge>
</TableCell>
<TableCell>{getAreaName(usu.areaId)}</TableCell>
<TableCell>{getGrupoName(usu.grupoId)}</TableCell>
<TableCell>{usu.email || '-'}</TableCell>
<TableCell>{usu.matriculaProfesional || '-'}</TableCell>
<TableCell>
@@ -245,8 +245,8 @@ export function GestionUsuarios() {
<div className="grid grid-cols-2 gap-2 text-sm pt-2 border-t border-gray-100 dark:border-gray-800">
<div>
<span className="text-xs text-gray-400 block">Área</span>
<span className="font-medium text-gray-700 dark:text-gray-300">{getAreaName(usu.areaId)}</span>
<span className="text-xs text-gray-400 block">Grupo</span>
<span className="font-medium text-gray-700 dark:text-gray-300">{getGrupoName(usu.grupoId)}</span>
</div>
<div>
<span className="text-xs text-gray-400 block">Matrícula</span>
@@ -329,14 +329,14 @@ export function GestionUsuarios() {
{form.rol !== 'admin' && (
<div className="space-y-2">
<Label>Área Asignada</Label>
<Select value={form.areaId} onValueChange={(v) => setForm({ ...form, areaId: v })}>
<Label>Grupo Asignado</Label>
<Select value={form.grupoId} onValueChange={(v) => setForm({ ...form, grupoId: v })}>
<SelectTrigger>
<SelectValue placeholder="Seleccionar área" />
</SelectTrigger>
<SelectContent>
{areas.map((area) => (
<SelectItem key={area.id} value={area.id}>{area.nombre}</SelectItem>
{grupos.map((grupo) => (
<SelectItem key={grupo.id} value={grupo.id}>{grupo.nombre}</SelectItem>
))}
</SelectContent>
</Select>
+12 -12
View File
@@ -10,7 +10,7 @@ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table';
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from '@/components/ui/dropdown-menu';
import { toast } from 'sonner';
import type { Internacion, Paciente, Cama, Area, Evolucion, Laboratorio, Cultivo } from '@/types';
import type { Internacion, Paciente, Cama, Grupo, Evolucion, Laboratorio, Cultivo } from '@/types';
import { formatDateDDMMYYYY, getNombreProfesional } from '@/lib/utils';
import { useHospitalStore } from '@/hooks/useHospitalStore';
@@ -21,7 +21,7 @@ interface InternacionesProps {
evoluciones?: Evolucion[];
laboratorios?: Laboratorio[];
cultivos?: Cultivo[];
areas: Area[];
grupos: Grupo[];
onIniciarInternacion: (internacion: Omit<Internacion, 'id' | 'activa'>) => void;
onFinalizarInternacion: (internacionId: string, datos: {
fechaEgreso: string;
@@ -42,7 +42,7 @@ export function Internaciones({
onFinalizarInternacion,
getPacienteById,
getCamaById,
areas,
grupos,
onVerHC,
onNuevoIngreso,
}: InternacionesProps) {
@@ -54,7 +54,7 @@ export function Internaciones({
const [busqueda, setBusqueda] = useState('');
const [pacienteSeleccionado, setPacienteSeleccionado] = useState<string>('');
const [camaSeleccionada, setCamaSeleccionada] = useState<string>('');
const [areaSeleccionada, setAreaSeleccionada] = useState<string>('');
const [grupoSeleccionada, setGrupoSeleccionada] = useState<string>('');
const [diagnosticoIngreso, setDiagnosticoIngreso] = useState('');
const [enfermedadActual, setEnfermedadActual] = useState('');
const [motivoConsulta, setMotivoConsulta] = useState('');
@@ -69,7 +69,7 @@ export function Internaciones({
const resetFormularioNueva = () => {
setPacienteSeleccionado('');
setCamaSeleccionada('');
setAreaSeleccionada('');
setGrupoSeleccionada('');
setDiagnosticoIngreso('');
setEnfermedadActual('');
setAntecedentesEnfermedadActual('');
@@ -83,12 +83,12 @@ export function Internaciones({
};
const handleIniciarInternacion = async () => {
if (pacienteSeleccionado && camaSeleccionada && areaSeleccionada && (diagnosticoIngreso || motivoConsulta) && enfermedadActual && effectiveMedico) {
if (pacienteSeleccionado && camaSeleccionada && grupoSeleccionada && (diagnosticoIngreso || motivoConsulta) && enfermedadActual && effectiveMedico) {
try {
await onIniciarInternacion({
pacienteId: pacienteSeleccionado,
camaId: camaSeleccionada,
areaId: areaSeleccionada,
grupoId: grupoSeleccionada,
diagnosticoIngreso: diagnosticoIngreso || motivoConsulta,
motivoConsulta,
enfermedadActual,
@@ -166,7 +166,7 @@ export function Internaciones({
return oa.suborden - ob.suborden;
});
const getAreaName = (areaId?: string) => areas.find(a => a.id === areaId)?.nombre;
const getGrupoName = (grupoId?: string) => grupos.find(a => a.id === grupoId)?.nombre;
const calcularEdad = (fechaNacimiento?: string) => {
if (!fechaNacimiento) return '-';
@@ -286,13 +286,13 @@ export function Internaciones({
)}
</div>
<div>
<Label>Área de Trabajo *</Label>
<Select value={areaSeleccionada} onValueChange={setAreaSeleccionada}>
<Label>Grupo de Trabajo *</Label>
<Select value={grupoSeleccionada} onValueChange={setGrupoSeleccionada}>
<SelectTrigger>
<SelectValue placeholder="Seleccionar área" />
</SelectTrigger>
<SelectContent>
{areas.filter(a => a.nombre !== 'Fuera de area').map(a => (
{grupos.filter(a => a.nombre !== 'Fuera de grupo').map(a => (
<SelectItem key={a.id} value={a.id}>
{a.nombre}
</SelectItem>
@@ -309,7 +309,7 @@ export function Internaciones({
<SelectContent>
{sortedCamas.filter(c => c.estado === 'Disponible').map(c => (
<SelectItem key={c.id} value={c.id}>
{c.numero} - {getAreaName(c.areaId) || 'Sin área'} ({c.tipo})
{c.numero} - {getGrupoName(c.grupoId) || 'Sin grupo'} ({c.tipo})
</SelectItem>
))}
</SelectContent>
+62 -62
View File
@@ -3,76 +3,76 @@ import { Bed, CheckCircle2, Clock, Wrench, User, Plus, Trash, Edit2, Save, X } f
import { Card, CardContent } from '@/components/ui/card';
import { Badge } from '@/components/ui/badge';
import { Button } from '@/components/ui/button';
import { formatDateDDMMYYYY, getNombreProfesional, isCamaFueraDeArea } from '@/lib/utils';
import { formatDateDDMMYYYY, getNombreProfesional, isCamaFueraDeGrupo } from '@/lib/utils';
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger } from '@/components/ui/dialog';
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
import { Label } from '@/components/ui/label';
import { Input } from '@/components/ui/input';
import type { Cama, Paciente, Internacion, Area } from '@/types';
import type { Cama, Paciente, Internacion, Grupo } from '@/types';
import { useHospitalStore } from '@/hooks/useHospitalStore';
interface MapaCamasProps {
camas: Cama[];
areas: Area[];
grupos: Grupo[];
pacientes: Paciente[];
internaciones: Internacion[];
onActualizarCama: (id: string, datos: Partial<Cama>) => void;
onAgregarCama: (cama: Omit<Cama, 'id'>) => any;
onEliminarCama: (id: string) => void;
onIniciarInternacion: (internacion: Omit<Internacion, 'id' | 'activa'>) => Promise<string>;
onAgregarArea: (area: Omit<Area, 'id'>) => any;
onActualizarArea: (id: string, datos: Partial<Area>) => void;
onEliminarArea: (id: string) => void;
onAgregarGrupo: (grupo: Omit<Grupo, 'id'>) => any;
onActualizarGrupo: (id: string, datos: Partial<Grupo>) => void;
onEliminarGrupo: (id: string) => void;
getPacienteById: (id: string) => Paciente | undefined;
getInternacionById: (id: string) => Internacion | undefined;
}
export function MapaCamas({
camas,
areas,
grupos,
pacientes,
internaciones,
onActualizarCama,
onAgregarCama,
onEliminarCama,
onIniciarInternacion,
onAgregarArea,
onActualizarArea,
onEliminarArea,
onAgregarGrupo,
onActualizarGrupo,
onEliminarGrupo,
getPacienteById,
getInternacionById
}: MapaCamasProps) {
const { canEditCama, canAccessArea, currentUser } = useHospitalStore();
const { canEditCama, canAccessGrupo, currentUser } = useHospitalStore();
const [filtroSala, setFiltroSala] = useState<string>('todas');
const [filtroTipo, setFiltroTipo] = useState<string>('todos');
const [filtroEstado, setFiltroEstado] = useState<string>('todos');
const [camaSeleccionada, setCamaSeleccionada] = useState<Cama | null>(null);
const [areaSeleccionada, setAreaSeleccionada] = useState<string>('');
const [grupoSeleccionada, setGrupoSeleccionada] = useState<string>('');
const [pacienteSeleccionado, setPacienteSeleccionado] = useState<string>('');
const [diagnostico, setDiagnostico] = useState('');
const [enfermedadActual, setEnfermedadActual] = useState('');
const [antecedentesEnfermedadActual, setAntecedentesEnfermedadActual] = useState('');
const effectiveMedico = getNombreProfesional(currentUser);
const [dialogoAbierto, setDialogoAbierto] = useState(false);
// Area dialog state
const [areaDialogOpen, setAreaDialogOpen] = useState(false);
const [areaNombre, setAreaNombre] = useState('');
const [editingAreaId, setEditingAreaId] = useState<string | null>(null);
// Grupo dialog state
const [grupoDialogOpen, setGrupoDialogOpen] = useState(false);
const [grupoNombre, setGrupoNombre] = useState('');
const [editingGrupoId, setEditingGrupoId] = useState<string | null>(null);
// Cama dialog state
const [bedDialogOpen, setBedDialogOpen] = useState(false);
const [editingBed, setEditingBed] = useState<Cama | null>(null);
const [bedNumero, setBedNumero] = useState('');
const [bedTipo, setBedTipo] = useState<Cama['tipo']>('General');
const [bedAreaId, setBedAreaId] = useState<string | undefined>(undefined);
const [bedGrupoId, setBedGrupoId] = useState<string | undefined>(undefined);
const salas = Array.from(new Set(camas.map(c => c.areaId).filter(Boolean))) as string[];
const areaById = Object.fromEntries(areas.map(a => [a.id, a.nombre]));
const salas = Array.from(new Set(camas.map(c => c.grupoId).filter(Boolean))) as string[];
const grupoById = Object.fromEntries(grupos.map(a => [a.id, a.nombre]));
const tipos = Array.from(new Set(camas.map(c => c.tipo)));
const camasFiltradas = camas.filter(cama => {
if (filtroSala !== 'todas' && cama.areaId !== filtroSala) return false;
if (filtroSala !== 'todas' && cama.grupoId !== filtroSala) return false;
if (filtroTipo !== 'todos' && cama.tipo !== filtroTipo) return false;
if (filtroEstado !== 'todos' && cama.estado !== filtroEstado) return false;
return true;
@@ -149,11 +149,11 @@ export function MapaCamas({
});
const handleOcuparCama = () => {
if (camaSeleccionada && areaSeleccionada && pacienteSeleccionado && diagnostico && enfermedadActual && effectiveMedico) {
if (camaSeleccionada && grupoSeleccionada && pacienteSeleccionado && diagnostico && enfermedadActual && effectiveMedico) {
onIniciarInternacion({
pacienteId: pacienteSeleccionado,
camaId: camaSeleccionada.id,
areaId: areaSeleccionada,
grupoId: grupoSeleccionada,
diagnosticoIngreso: diagnostico,
enfermedadActual,
antecedentesEnfermedadActual: antecedentesEnfermedadActual || undefined,
@@ -161,7 +161,7 @@ export function MapaCamas({
});
setDialogoAbierto(false);
setCamaSeleccionada(null);
setAreaSeleccionada('');
setGrupoSeleccionada('');
setPacienteSeleccionado('');
setDiagnostico('');
setEnfermedadActual('');
@@ -187,24 +187,24 @@ export function MapaCamas({
</div>
<div className="flex items-center gap-2">
<Badge variant="outline" className="bg-green-50 text-green-700 dark:bg-green-900 dark:text-green-300 dark:border-green-700">
{camas.filter(c => c.estado === 'Disponible' && !isCamaFueraDeArea(c, areas)).length} Disponibles
{camas.filter(c => c.estado === 'Disponible' && !isCamaFueraDeGrupo(c, grupos)).length} Disponibles
</Badge>
<Badge variant="outline" className="bg-red-50 text-red-700 dark:bg-red-900 dark:text-red-300 dark:border-red-700">
{camas.filter(c => c.estado === 'Ocupada' && !isCamaFueraDeArea(c, areas)).length} Ocupadas
{camas.filter(c => c.estado === 'Ocupada' && !isCamaFueraDeGrupo(c, grupos)).length} Ocupadas
</Badge>
<div className="ml-4 flex items-center gap-2 flex-wrap sm:flex-nowrap">
<Button variant="outline" onClick={() => {
setEditingAreaId(null);
setAreaNombre('');
setAreaDialogOpen(true);
setEditingGrupoId(null);
setGrupoNombre('');
setGrupoDialogOpen(true);
}} className="text-xs px-2 py-1 sm:text-sm sm:px-4 sm:py-2">
Administrar Áreas
Administrar Grupos
</Button>
<Button variant="outline" onClick={() => {
setEditingBed(null);
setBedNumero('');
setBedTipo('General');
setBedAreaId(areas?.[0]?.id);
setBedGrupoId(grupos?.[0]?.id);
setBedDialogOpen(true);
}} className="text-xs px-2 py-1 sm:text-sm sm:px-4 sm:py-2">
<Plus className="h-3 w-3 sm:h-4 sm:w-4 sm:mr-1" />
@@ -219,15 +219,15 @@ export function MapaCamas({
<CardContent className="p-4">
<div className="grid grid-cols-1 sm:grid-cols-3 gap-4">
<div>
<Label className="text-xs text-gray-500 mb-1 block">Área</Label>
<Label className="text-xs text-gray-500 mb-1 block">Grupo / Área</Label>
<Select value={filtroSala} onValueChange={setFiltroSala}>
<SelectTrigger>
<SelectValue placeholder="Todas las áreas" />
<SelectValue placeholder="Todos los grupos" />
</SelectTrigger>
<SelectContent>
<SelectItem value="todas">Todas las áreas</SelectItem>
<SelectItem value="todas">Todos los grupos</SelectItem>
{salas.map(sala => (
<SelectItem key={sala} value={sala}>{areaById[sala] ?? sala}</SelectItem>
<SelectItem key={sala} value={sala}>{grupoById[sala] ?? sala}</SelectItem>
))}
</SelectContent>
</Select>
@@ -284,7 +284,7 @@ export function MapaCamas({
{getEstadoIcono(cama)}
</div>
<p className="font-bold text-sm sm:text-lg">{cama.numero}</p>
<p className="text-xs opacity-75 truncate">{cama.areaId ? areaById[cama.areaId] ?? 'Área desconocida' : 'Sin área'}</p>
<p className="text-xs opacity-75 truncate">{cama.grupoId ? grupoById[cama.grupoId] ?? 'Grupo desconocido' : 'Sin grupo'}</p>
<Badge variant="outline" className="mt-1 sm:mt-2 text-xs bg-white/50 dark:bg-gray-800 dark:text-gray-300">
{cama.tipo}
</Badge>
@@ -305,7 +305,7 @@ export function MapaCamas({
<DialogHeader>
<DialogTitle className="flex items-center gap-2">
<Bed className="h-5 w-5" />
Cama {cama.numero} - {cama.areaId ? areaById[cama.areaId] ?? 'Área desconocida' : 'Sin área'}
Cama {cama.numero} - {cama.grupoId ? grupoById[cama.grupoId] ?? 'Grupo desconocido' : 'Sin grupo'}
</DialogTitle>
</DialogHeader>
<div className="space-y-4">
@@ -365,13 +365,13 @@ export function MapaCamas({
</Select>
</div>
<div>
<Label>Área de Trabajo *</Label>
<Select value={areaSeleccionada} onValueChange={setAreaSeleccionada}>
<Label>Grupo de Trabajo *</Label>
<Select value={grupoSeleccionada} onValueChange={setGrupoSeleccionada}>
<SelectTrigger>
<SelectValue placeholder="Seleccionar área" />
</SelectTrigger>
<SelectContent>
{areas.filter(a => a.nombre !== 'Fuera de area').map(a => (
{grupos.filter(a => a.nombre !== 'Fuera de grupo').map(a => (
<SelectItem key={a.id} value={a.id}>
{a.nombre}
</SelectItem>
@@ -471,7 +471,7 @@ export function MapaCamas({
setEditingBed(cama);
setBedNumero(cama.numero);
setBedTipo(cama.tipo);
setBedAreaId(cama.areaId);
setBedGrupoId(cama.grupoId);
setBedDialogOpen(true);
}}>
<Edit2 className="h-4 w-4 mr-1" />Editar
@@ -496,46 +496,46 @@ export function MapaCamas({
})}
</div>
{/* Area dialog */}
<Dialog open={areaDialogOpen} onOpenChange={setAreaDialogOpen}>
{/* Grupo dialog */}
<Dialog open={grupoDialogOpen} onOpenChange={setGrupoDialogOpen}>
<DialogContent className="sm:max-w-md">
<DialogHeader>
<DialogTitle>{editingAreaId ? 'Editar Área' : 'Nueva Área'}</DialogTitle>
<DialogTitle>{editingGrupoId ? 'Editar Grupo' : 'Nuevo Grupo'}</DialogTitle>
</DialogHeader>
<div className="space-y-4">
<div>
<Label>Nombre</Label>
<Input value={areaNombre} onChange={(e) => setAreaNombre(e.target.value)} />
<Input value={grupoNombre} onChange={(e) => setGrupoNombre(e.target.value)} />
</div>
<div className="flex gap-2 justify-end">
{editingAreaId && (
<Button size="sm" variant="destructive" onClick={() => { if (confirm('Eliminar área?')) { onEliminarArea(editingAreaId); setAreaDialogOpen(false); } }}>
{editingGrupoId && (
<Button size="sm" variant="destructive" onClick={() => { if (confirm('Eliminar grupo?')) { onEliminarGrupo(editingGrupoId); setGrupoDialogOpen(false); } }}>
<Trash className="h-4 w-4" />
</Button>
)}
<Button size="sm" variant="outline" onClick={() => setAreaDialogOpen(false)}>
<Button size="sm" variant="outline" onClick={() => setGrupoDialogOpen(false)}>
<X className="h-4 w-4" />
</Button>
<Button size="sm" variant="outline" onClick={() => {
if (!areaNombre.trim()) return alert('Nombre requerido');
if (editingAreaId) onActualizarArea(editingAreaId, { nombre: areaNombre });
else onAgregarArea({ nombre: areaNombre });
setAreaDialogOpen(false);
if (!grupoNombre.trim()) return alert('Nombre requerido');
if (editingGrupoId) onActualizarGrupo(editingGrupoId, { nombre: grupoNombre });
else onAgregarGrupo({ nombre: grupoNombre });
setGrupoDialogOpen(false);
}}>
<Save className="h-4 w-4" />
</Button>
</div>
<div>
<p className="text-sm font-medium">Áreas existentes</p>
<p className="text-sm font-medium">Grupos existentes</p>
<div className="mt-2 space-y-2">
{areas.map(a => (
{grupos.map(a => (
<div key={a.id} className="flex items-center justify-between">
<div>{a.nombre}</div>
<div className="flex gap-1">
<Button size="sm" variant="outline" onClick={() => { setEditingAreaId(a.id); setAreaNombre(a.nombre); }}>
<Button size="sm" variant="outline" onClick={() => { setEditingGrupoId(a.id); setGrupoNombre(a.nombre); }}>
<Edit2 className="h-4 w-4" />
</Button>
<Button size="sm" variant="outline" className="text-red-600" onClick={() => { if (confirm('Eliminar área?')) onEliminarArea(a.id); }}>
<Button size="sm" variant="outline" className="text-red-600" onClick={() => { if (confirm('Eliminar grupo?')) onEliminarGrupo(a.id); }}>
<Trash className="h-4 w-4" />
</Button>
</div>
@@ -574,14 +574,14 @@ export function MapaCamas({
</Select>
</div>
<div>
<Label>Área</Label>
<Select value={bedAreaId ?? '__none'} onValueChange={(v) => setBedAreaId(v === '__none' ? undefined : v)}>
<Label>Grupo / Área</Label>
<Select value={bedGrupoId ?? '__none'} onValueChange={(v) => setBedGrupoId(v === '__none' ? undefined : v)}>
<SelectTrigger>
<SelectValue placeholder="Seleccione área" />
<SelectValue placeholder="Seleccione grupo" />
</SelectTrigger>
<SelectContent>
<SelectItem value="__none">Sin área</SelectItem>
{areas.map(a => (
<SelectItem value="__none">Sin grupo</SelectItem>
{grupos.map(a => (
<SelectItem key={a.id} value={a.id}>{a.nombre}</SelectItem>
))}
</SelectContent>
@@ -597,9 +597,9 @@ export function MapaCamas({
<Button variant="outline" onClick={() => {
if (!bedNumero.trim()) return alert('Número requerido');
if (editingBed) {
onActualizarCama(editingBed.id, { numero: bedNumero, tipo: bedTipo, areaId: bedAreaId });
onActualizarCama(editingBed.id, { numero: bedNumero, tipo: bedTipo, grupoId: bedGrupoId });
} else {
onAgregarCama({ numero: bedNumero, tipo: bedTipo, estado: 'Disponible', areaId: bedAreaId });
onAgregarCama({ numero: bedNumero, tipo: bedTipo, estado: 'Disponible', grupoId: bedGrupoId });
}
setBedDialogOpen(false);
}}>Guardar</Button>
+15 -15
View File
@@ -7,23 +7,23 @@ import { Label } from '@/components/ui/label';
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
import { Badge } from '@/components/ui/badge';
import { toast } from 'sonner';
import type { Paciente, Cama, Area, Internacion } from '@/types';
import type { Paciente, Cama, Grupo, Internacion } from '@/types';
import { getNombreProfesional } from '@/lib/utils';
import { useHospitalStore } from '@/hooks/useHospitalStore';
interface NuevoIngresoProps {
pacientes: Paciente[];
camas: Cama[];
areas: Area[];
grupos: Grupo[];
onIniciarInternacion: (internacion: Omit<Internacion, 'id' | 'activa'>) => Promise<string>;
onAgregarCama?: (cama: Omit<Cama, 'id'>) => Promise<string>;
onAgregarPaciente?: (paciente: Omit<Paciente, 'id'>) => void;
onActualizarPaciente?: (id: string, datos: Partial<Paciente>) => void;
onVolver: () => void;
getAreaName: (areaId: string | undefined) => string;
getGrupoName: (grupoId: string | undefined) => string;
}
export function NuevoIngreso({ pacientes, camas, areas, onIniciarInternacion, onAgregarCama, onVolver, getAreaName }: NuevoIngresoProps) {
export function NuevoIngreso({ pacientes, camas, grupos, onIniciarInternacion, onAgregarCama, onVolver, getGrupoName }: NuevoIngresoProps) {
const { currentUser } = useHospitalStore();
const [pacienteSeleccionado, setPacienteSeleccionado] = useState<string>('');
const [busqueda, setBusqueda] = useState('');
@@ -58,8 +58,8 @@ export function NuevoIngreso({ pacientes, camas, areas, onIniciarInternacion, on
const normalizeStr = (str?: string) => (str || '').normalize('NFD').replace(/[\u0300-\u036f]/g, '').trim().toLowerCase();
const areaFueraDeArea = areas.find(a => normalizeStr(a.nombre) === 'fuera de area');
const areaFueraDeAreaId = areaFueraDeArea?.id || areas[0]?.id || 'fuera-de-area';
const grupoFueraDeGrupo = grupos.find(a => normalizeStr(a.nombre) === 'fuera de grupo');
const grupoFueraDeGrupoId = grupoFueraDeGrupo?.id || grupos[0]?.id || 'fuera-de-grupo';
const pacientesSinInternar = pacientes;
@@ -142,22 +142,22 @@ export function NuevoIngreso({ pacientes, camas, areas, onIniciarInternacion, on
try {
let camaId = '';
let newAreaId = '';
let newGrupoId = '';
if (modoCama === 'escribir') {
const numeroCama = camaInput.trim();
const camaExistente = camas.find(c => c.numero === numeroCama);
if (camaExistente) {
camaId = camaExistente.id;
newAreaId = camaExistente.areaId || areaFueraDeAreaId;
newGrupoId = camaExistente.grupoId || grupoFueraDeGrupoId;
} else if (onAgregarCama) {
camaId = await onAgregarCama({
numero: numeroCama,
areaId: areaFueraDeAreaId,
grupoId: grupoFueraDeGrupoId,
tipo: 'General',
estado: 'Ocupada'
});
newAreaId = areaFueraDeAreaId;
newGrupoId = grupoFueraDeGrupoId;
} else {
toast.error('No se puede crear la cama');
setIsSubmitting(false);
@@ -171,13 +171,13 @@ export function NuevoIngreso({ pacientes, camas, areas, onIniciarInternacion, on
return;
}
camaId = camaSeleccionada;
newAreaId = camaElegida.areaId || areaFueraDeAreaId;
newGrupoId = camaElegida.grupoId || grupoFueraDeGrupoId;
}
await onIniciarInternacion({
pacienteId: pacienteSeleccionado,
camaId: camaId,
areaId: newAreaId,
grupoId: newGrupoId,
medicoIngresante: effectiveMedico.trim(),
diagnosticoIngreso: diagnosticoIngreso.trim(),
motivoConsulta: motivoConsulta.trim() || undefined,
@@ -293,13 +293,13 @@ export function NuevoIngreso({ pacientes, camas, areas, onIniciarInternacion, on
</Card>
</div>
{/* Columna 2: Cama y Área */}
{/* Columna 2: Cama y Grupo */}
<div className="space-y-6">
<Card className="min-h-[200px]">
<CardContent className="pt-6 space-y-4">
<h3 className="font-semibold text-gray-900 dark:text-white flex items-center gap-2">
<Bed className="h-4 w-4" />
Asignación de Cama y Área
Asignación de Cama y Grupo
</h3>
<div className="flex flex-col sm:flex-row gap-2 mb-4">
@@ -330,7 +330,7 @@ export function NuevoIngreso({ pacientes, camas, areas, onIniciarInternacion, on
<SelectContent>
{sortedCamas.map(c => (
<SelectItem key={c.id} value={c.id}>
{c.numero} - {getAreaName(c.areaId)}
{c.numero} - {getGrupoName(c.grupoId)}
</SelectItem>
))}
</SelectContent>
+11 -6
View File
@@ -21,9 +21,17 @@ export interface Paciente {
}
export interface Grupo {
id: string;
nombre: string;
}
export type Area = Grupo;
export interface Cama {
id: string;
numero: string;
grupoId?: string;
areaId?: string;
tipo: 'General' | 'Aislamiento KPC' | 'Aislamiento COVID' | 'Aislamiento Clostridium' | 'Aislamiento Neutropenico';
estado: 'Disponible' | 'Ocupada' | 'Reparacion' | 'Reservada';
@@ -31,16 +39,12 @@ export interface Cama {
internacionId?: string;
}
export interface Area {
id: string;
nombre: string;
}
export interface Internacion {
id: string;
pacienteId: string;
camaId: string;
areaId: string;
grupoId?: string;
areaId?: string;
fechaIngresoHospital?: string;
fechaIngresoClinica?: string;
fechaEgreso?: string;
@@ -229,6 +233,7 @@ export interface Usuario {
rol: RolUsuario;
matriculaProfesional?: string;
passwordHash: string;
grupoId?: string;
areaId?: string;
fechaCreacion: string;
}