feat: implementar permisos por área de trabajo

- Agregar funciones de verificación de área en useHospitalStore
- canEditInArea, canEditCama, canEditInternacion, canEditPaciente
- Leer datos de tablas individuales en lugar de tabla kv
- Usar sessionStorage para persistir sesión de usuario
- Agregar prop canEdit a componentes de secciones en HC
- Ocultar botones de edición para usuarios sin permisos en el área
- MapaCamas: ocultar botones de edición para camas fuera del área
This commit is contained in:
2026-04-23 02:18:42 -03:00
parent 76e01bfd79
commit e6e1582b25
8 changed files with 411 additions and 118 deletions
+91
View File
@@ -132,3 +132,94 @@ export async function verifyPassword(dni, password) {
export async function hashPassword(password) { export async function hashPassword(password) {
return bcrypt.hashSync(password, 10); return bcrypt.hashSync(password, 10);
} }
export async function getAllPacientes() {
const db = await openDb();
const rows = await db.all('SELECT * FROM pacientes');
await db.close();
return rows;
}
export async function getAllAreas() {
const db = await openDb();
const rows = await db.all('SELECT * FROM areas');
await db.close();
return rows;
}
export async function getAllCamas() {
const db = await openDb();
const rows = await db.all('SELECT * FROM camas');
await db.close();
return rows;
}
export async function getAllInternaciones() {
const db = await openDb();
const rows = await db.all('SELECT * FROM internaciones');
await db.close();
return rows;
}
export async function getAllEvoluciones() {
const db = await openDb();
const rows = await db.all('SELECT * FROM evoluciones');
await db.close();
return rows;
}
export async function getAllLaboratorios() {
const db = await openDb();
const rows = await db.all('SELECT * FROM laboratorios');
await db.close();
return rows;
}
export async function getAllAcidosBase() {
const db = await openDb();
const rows = await db.all('SELECT * FROM acidosbase');
await db.close();
return rows;
}
export async function getAllCultivos() {
const db = await openDb();
const rows = await db.all('SELECT * FROM cultivos');
await db.close();
return rows;
}
export async function getAllEstudiosComplementarios() {
const db = await openDb();
const rows = await db.all('SELECT * FROM estudiosComplementarios');
await db.close();
return rows;
}
export async function getAllInterconsultas() {
const db = await openDb();
const rows = await db.all('SELECT * FROM interconsultas');
await db.close();
return rows;
}
export async function getAllAtb() {
const db = await openDb();
const rows = await db.all('SELECT * FROM atb');
await db.close();
return rows;
}
export async function getAllIndicaciones() {
const db = await openDb();
const rows = await db.all('SELECT * FROM indicaciones');
await db.close();
return rows;
}
export async function getAllMovimientosIndicaciones() {
const db = await openDb();
const rows = await db.all('SELECT * FROM movimientos_indicaciones');
await db.close();
return rows;
}
+152 -35
View File
@@ -1,6 +1,6 @@
import express from 'express'; import express from 'express';
import cors from 'cors'; import cors from 'cors';
import { getValue, setValue, getUsuarioByDni, getUsuarioById, getAllUsuarios, createUsuario, updateUsuario, deleteUsuario, verifyPassword, hashPassword } from './db.js'; import { openDb, getValue, setValue, getUsuarioByDni, getUsuarioById, getAllUsuarios, createUsuario, updateUsuario, deleteUsuario, verifyPassword, hashPassword, getAllPacientes, getAllAreas, getAllCamas, getAllInternaciones, getAllEvoluciones, getAllLaboratorios, getAllAcidosBase, getAllCultivos, getAllEstudiosComplementarios, getAllInterconsultas, getAllAtb, getAllIndicaciones, getAllMovimientosIndicaciones } from './db.js';
const app = express(); const app = express();
app.use(cors()); app.use(cors());
@@ -11,9 +11,28 @@ const STORAGE_KEY = 'hospital-data-v1';
app.get('/api/state', async (req, res) => { app.get('/api/state', async (req, res) => {
try { try {
const v = await getValue(STORAGE_KEY); const state = {
if (!v) return res.json(null); pacientes: await getAllPacientes(),
res.json(JSON.parse(v)); areas: await getAllAreas(),
camas: await getAllCamas(),
internaciones: (await getAllInternaciones()).map(i => ({ ...i, activa: !!i.activa })),
evoluciones: (await getAllEvoluciones()).map(e => ({
...e,
signosVitales: typeof e.signosVitales === 'string' ? JSON.parse(e.signosVitales) : e.signosVitales,
examenFisico: typeof e.examenFisico === 'string' ? JSON.parse(e.examenFisico) : e.examenFisico
})),
laboratorios: (await getAllLaboratorios()).map(l => ({ ...l, resultados: typeof l.resultados === 'string' ? JSON.parse(l.resultados) : l.resultados })),
acidosBase: await getAllAcidosBase(),
cultivos: await getAllCultivos(),
estudiosComplementarios: await getAllEstudiosComplementarios(),
interconsultas: (await getAllInterconsultas()).map(ic => ({ ...ic, realizada: !!ic.realizada })),
atb: await getAllAtb(),
indicaciones: await getAllIndicaciones(),
movimientosIndicaciones: await getAllMovimientosIndicaciones(),
vistaActual: 'dashboard',
currentInternacionId: null
};
res.json(state);
} catch (err) { } catch (err) {
console.error(err); console.error(err);
res.status(500).json({ error: 'failed to read state' }); res.status(500).json({ error: 'failed to read state' });
@@ -22,8 +41,135 @@ app.get('/api/state', async (req, res) => {
app.put('/api/state', async (req, res) => { app.put('/api/state', async (req, res) => {
try { try {
const body = req.body; const state = req.body;
await setValue(STORAGE_KEY, JSON.stringify(body)); const db = await openDb();
await db.run('DELETE FROM evoluciones');
await db.run('DELETE FROM acidosbase');
await db.run('DELETE FROM cultivos');
await db.run('DELETE FROM laboratorios');
await db.run('DELETE FROM internaciones');
await db.run('DELETE FROM camas');
await db.run('DELETE FROM areas');
await db.run('DELETE FROM pacientes');
await db.run('DELETE FROM estudiosComplementarios');
await db.run('DELETE FROM interconsultas');
await db.run('DELETE FROM atb');
await db.run('DELETE FROM indicaciones');
await db.run('DELETE FROM movimientos_indicaciones');
if (state.pacientes?.length) {
for (const p of state.pacientes) {
await db.run(
'INSERT INTO pacientes (id, apellido, nombre, dni, fechaNacimiento, sexo, telefono, email, direccion, obraSocial, nacionalidad, medicacionHabitual, antecedentes, alergias, grupoSanguineo, historiaClinica) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)',
[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 (state.areas?.length) {
for (const a of state.areas) {
await db.run('INSERT INTO areas (id, nombre) VALUES (?, ?)', [a.id, a.nombre]);
}
}
if (state.camas?.length) {
for (const c of state.camas) {
await db.run('INSERT INTO camas (id, numero, areaId, tipo, estado) VALUES (?, ?, ?, ?, ?)', [c.id, c.numero, c.areaId, c.tipo, c.estado]);
}
}
if (state.internaciones?.length) {
for (const i of state.internaciones) {
await db.run(
'INSERT INTO internaciones (id, pacienteId, camaId, fechaIngresoHospital, fechaIngresoClinica, fechaEgreso, diagnosticoIngreso, motivoConsulta, enfermedadActual, antecedentesEnfermedadActual, diagnosticoEgreso, medicoIngresante, motivoEgreso, activa, apache, derivacion) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)',
[i.id, i.pacienteId, i.camaId, 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) {
for (const e of state.evoluciones) {
await db.run(
'INSERT INTO evoluciones (id, internacionId, fecha, hora, medico, signosVitales, examenFisico, novedades, comentarios, pendientes) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)',
[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) {
for (const l of state.laboratorios) {
await db.run(
'INSERT INTO laboratorios (id, pacienteId, fecha, hora, tipo, resultados, observaciones) VALUES (?, ?, ?, ?, ?, ?, ?)',
[l.id, l.pacienteId, l.fecha, l.hora || null, l.tipo, JSON.stringify(l.resultados), l.observaciones || null]
);
}
}
if (state.acidosBase?.length) {
for (const a of state.acidosBase) {
await db.run(
'INSERT INTO acidosbase (id, pacienteId, fecha, hora, ph, pco2, po2, hco3, be, sato2, lactato, interpretacion, fio2) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)',
[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) {
for (const c of state.cultivos) {
await db.run(
'INSERT INTO cultivos (id, pacienteId, internacionId, fechaToma, protocolo, tipoMuestra, observaciones, estado, fechaResultado, germen, sensible, resistente) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)',
[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) {
for (const e of state.estudiosComplementarios) {
await db.run(
'INSERT INTO estudiosComplementarios (id, pacienteId, internacionId, fecha, tipo, resultado) VALUES (?, ?, ?, ?, ?, ?)',
[e.id, e.pacienteId, e.internacionId || null, e.fecha, e.tipo, e.resultado]
);
}
}
if (state.interconsultas?.length) {
for (const ic of state.interconsultas) {
await db.run(
'INSERT INTO interconsultas (id, pacienteId, internacionId, fecha, servicioInterconsultado, motivo, respuestaInterconsulta, respuestaFecha) VALUES (?, ?, ?, ?, ?, ?, ?, ?)',
[ic.id, ic.pacienteId, ic.internacionId, ic.fecha, ic.servicioInterconsultado, ic.motivo || null, ic.respuestaInterconsulta || null, ic.respuestaFecha || null]
);
}
}
if (state.atb?.length) {
for (const atb of state.atb) {
await db.run(
'INSERT INTO atb (id, pacienteId, internacionId, antibiotico, fechaInicio, fechaFinalizacion) VALUES (?, ?, ?, ?, ?, ?)',
[atb.id, atb.pacienteId, atb.internacionId, atb.antibiotico, atb.fechaInicio, atb.fechaFinalizacion || null]
);
}
}
if (state.indicaciones?.length) {
for (const ind of state.indicaciones) {
await db.run(
'INSERT INTO indicaciones (id, internacionId, tipo, droga, dosis, frecuenciaHoras, via, tipoPlan, tipoPlan2, cantidadMl, cantidadMl2, tiempoHoras, estado, medicoCrea, fechaCrea, tipoInsulina, unidadesDesayuno, unidadesAlmuerzo, unidadesNoche, indicacionNoFco) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)',
[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) {
for (const mov of state.movimientosIndicaciones) {
await db.run(
'INSERT INTO movimientos_indicaciones (id, indicacionId, internacionId, tipo, fecha, profesional, indicacionPrevia, indicacionNueva) VALUES (?, ?, ?, ?, ?, ?, ?, ?)',
[mov.id, mov.indicacionId, mov.internacionId, mov.tipo, mov.fecha, mov.profesional, mov.indicacionPrevia || null, mov.indicacionNueva || null]
);
}
}
await db.close();
res.json({ ok: true }); res.json({ ok: true });
} catch (err) { } catch (err) {
console.error(err); console.error(err);
@@ -35,35 +181,6 @@ app.listen(PORT, () => {
console.log(`API server listening on http://localhost:${PORT}`); console.log(`API server listening on http://localhost:${PORT}`);
}); });
// Crear usuario admin inicial si no existe
async function initAdminUser() {
try {
const adminExists = await getUsuarioByDni('12345678');
if (!adminExists) {
const passwordHash = await hashPassword('admin123');
const adminUser = {
id: generateUUID(),
apellido: 'Admin',
nombre: 'Sistema',
dni: '12345678',
fechaNacimiento: '1970-01-01',
email: 'admin@hospital.gob.ar',
rol: 'admin',
matriculaProfesional: null,
passwordHash,
areaId: null,
fechaCreacion: new Date().toISOString().split('T')[0]
};
await createUsuario(adminUser);
console.log('Usuario admin creado: DNI 12345678, Password admin123');
}
} catch (err) {
console.error('Error creating admin user:', err);
}
}
initAdminUser();
// === AUTENTICACIÓN === // === AUTENTICACIÓN ===
function generateUUID() { function generateUUID() {
+1
View File
@@ -183,6 +183,7 @@ function AppContent() {
onActualizarCama={store.actualizarCama} onActualizarCama={store.actualizarCama}
onVolver={() => store.setVista('internaciones')} onVolver={() => store.setVista('internaciones')}
onEditarIngreso={() => { store.setVista('editaringreso'); }} onEditarIngreso={() => { store.setVista('editaringreso'); }}
canEdit={store.canEditInternacion(internacion.id)}
/> />
); );
} }
+69 -8
View File
@@ -88,6 +88,8 @@ export function useHospitalStore() {
const body = await res.json(); const body = await res.json();
if (mounted && body) { if (mounted && body) {
const defaults = defaultState(); const defaults = defaultState();
const storedUser = sessionStorage.getItem('hospital_user');
const currentUser = storedUser ? JSON.parse(storedUser) : null;
const normalized = { const normalized = {
...defaults, ...defaults,
...body, ...body,
@@ -95,6 +97,8 @@ export function useHospitalStore() {
interconsultas: body.interconsultas || [], interconsultas: body.interconsultas || [],
atb: body.atb || [], atb: body.atb || [],
movimientosIndicaciones: body.movimientosIndicaciones || [], movimientosIndicaciones: body.movimientosIndicaciones || [],
currentUser,
isAuthenticated: !!currentUser,
}; };
setState(normalized); setState(normalized);
} }
@@ -108,15 +112,24 @@ export function useHospitalStore() {
}, []); }, []);
// Persist state to backend when it changes (debounced) // Persist state to backend when it changes (debounced)
const [initialLoadComplete, setInitialLoadComplete] = useState(false);
useEffect(() => { useEffect(() => {
if (!isLoaded) return; if (isLoaded && !initialLoadComplete) {
setInitialLoadComplete(true);
}
}, [isLoaded, initialLoadComplete]);
useEffect(() => {
if (!initialLoadComplete) return;
const t = setTimeout(() => { const t = setTimeout(() => {
(async () => { (async () => {
try { try {
const { currentUser, isAuthenticated, ...stateToSave } = state;
await fetch(`${API_BASE}/state`, { await fetch(`${API_BASE}/state`, {
method: 'PUT', method: 'PUT',
headers: { 'Content-Type': 'application/json' }, headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(state), body: JSON.stringify(stateToSave),
}); });
} catch (err) { } catch (err) {
// ignore save errors for now // ignore save errors for now
@@ -124,7 +137,7 @@ export function useHospitalStore() {
})(); })();
}, 300); }, 300);
return () => clearTimeout(t); return () => clearTimeout(t);
}, [state, isLoaded]); }, [state, initialLoadComplete]);
// Acciones de navegación // Acciones de navegación
const setVista = useCallback((vista: Vista) => { const setVista = useCallback((vista: Vista) => {
@@ -570,6 +583,7 @@ const getEstadisticas = useCallback(() => {
throw new Error(err.error || 'Error en autenticación'); throw new Error(err.error || 'Error en autenticación');
} }
const user = await res.json(); const user = await res.json();
sessionStorage.setItem('hospital_user', JSON.stringify(user));
setState(prev => ({ ...prev, currentUser: user, isAuthenticated: true })); setState(prev => ({ ...prev, currentUser: user, isAuthenticated: true }));
return user; return user;
} catch (err) { } catch (err) {
@@ -578,13 +592,9 @@ const getEstadisticas = useCallback(() => {
}, []); }, []);
const logout = useCallback(() => { const logout = useCallback(() => {
sessionStorage.removeItem('hospital_user');
setState(prev => { setState(prev => {
const newState = { ...prev, currentUser: null, isAuthenticated: false }; const newState = { ...prev, currentUser: null, isAuthenticated: false };
fetch(`${API_BASE}/state`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ ...newState }),
}).catch(() => {});
return newState; return newState;
}); });
}, []); }, []);
@@ -644,6 +654,50 @@ const getEstadisticas = useCallback(() => {
return rol === 'admin'; return rol === 'admin';
}, [state.currentUser, state.internaciones, state.camas]); }, [state.currentUser, state.internaciones, state.camas]);
const getCamaAreaId = useCallback((camaId: string): string | null => {
const cama = state.camas.find(c => c.id === camaId);
return cama?.areaId || null;
}, [state.camas]);
const getInternacionAreaId = useCallback((internacionId: string): string | null => {
const internacion = state.internaciones.find(i => i.id === internacionId);
if (!internacion) return null;
return getCamaAreaId(internacion.camaId);
}, [state.internaciones, getCamaAreaId]);
const getPacienteAreaId = 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);
return null;
}
return getCamaAreaId(internacion.camaId);
}, [state.internaciones, getCamaAreaId]);
const canEditInArea = useCallback((areaId: string | null | undefined): boolean => {
const user = state.currentUser;
if (!user) return false;
if (user.rol === 'admin') return true;
if (!areaId) return false;
return user.areaId === areaId;
}, [state.currentUser]);
const canEditCama = useCallback((camaId: string): boolean => {
const areaId = getCamaAreaId(camaId);
return canEditInArea(areaId);
}, [getCamaAreaId, canEditInArea]);
const canEditInternacion = useCallback((internacionId: string): boolean => {
const areaId = getInternacionAreaId(internacionId);
return canEditInArea(areaId);
}, [getInternacionAreaId, canEditInArea]);
const canEditPaciente = useCallback((pacienteId: string): boolean => {
const areaId = getPacienteAreaId(pacienteId);
return canEditInArea(areaId);
}, [getPacienteAreaId, canEditInArea]);
return { return {
...state, ...state,
isLoaded, isLoaded,
@@ -702,5 +756,12 @@ const getEstadisticas = useCallback(() => {
updateEmail, updateEmail,
hasPermission, hasPermission,
canAccessInternacion, canAccessInternacion,
canEditInArea,
canEditCama,
canEditInternacion,
canEditPaciente,
getCamaAreaId,
getInternacionAreaId,
getPacienteAreaId,
}; };
} }
+42 -27
View File
@@ -69,6 +69,7 @@ interface HistoriaClinicaProps {
interconsultas: Interconsulta[]; interconsultas: Interconsulta[];
atb: ATB[]; atb: ATB[];
indicadores: Indicacion[]; indicadores: Indicacion[];
canEdit: boolean;
onAgregarEvolucion: (evolucion: Omit<Evolucion, 'id'>) => void; onAgregarEvolucion: (evolucion: Omit<Evolucion, 'id'>) => void;
onActualizarEvolucion: (id: string, datos: Partial<Evolucion>) => void; onActualizarEvolucion: (id: string, datos: Partial<Evolucion>) => void;
onEliminarEvolucion: (id: string) => void; onEliminarEvolucion: (id: string) => void;
@@ -92,11 +93,8 @@ interface HistoriaClinicaProps {
onActualizarATB: (id: string, datos: Partial<ATB>) => void; onActualizarATB: (id: string, datos: Partial<ATB>) => void;
onEliminarATB: (id: string) => void; onEliminarATB: (id: string) => void;
onAgregarIndicacion: (indicacion: Omit<Indicacion, 'id'>) => void; onAgregarIndicacion: (indicacion: Omit<Indicacion, 'id'>) => void;
onActualizarIndicacion: (id: string, datos: Partial<Indicacion>) => void; onActualizarIndicacion: (indicacion: Indicacion) => void;
onEliminarIndicacion: (id: string) => void; onEliminarIndicacion: (id: string) => void;
movimientos?: any[];
onAgregarMovimiento?: (m: any) => void;
onActualizarInternacion: (id: string, datos: Partial<Internacion>) => void;
onActualizarCama: (id: string, datos: Partial<Cama>) => void; onActualizarCama: (id: string, datos: Partial<Cama>) => void;
onVolver: () => void; onVolver: () => void;
onEditarIngreso?: () => void; onEditarIngreso?: () => void;
@@ -535,19 +533,19 @@ export function HistoriaClinica({
</ScrollArea> </ScrollArea>
<TabsContent value="laboratorios" className="mt-4 min-w-0 w-full overflow-hidden"> <TabsContent value="laboratorios" className="mt-4 min-w-0 w-full overflow-hidden">
<SeccionLaboratorios lab={laboratorios} patientId={paciente.id} add={onAgregarLaboratorio} update={onActualizarLaboratorio} del={onEliminarLaboratorio} addAcidoBase={onAgregarAcidoBase} /> <SeccionLaboratorios lab={laboratorios} patientId={paciente.id} add={onAgregarLaboratorio} update={onActualizarLaboratorio} del={onEliminarLaboratorio} addAcidoBase={onAgregarAcidoBase} canEdit={canEdit} />
</TabsContent> </TabsContent>
<TabsContent value="evoluciones" className="mt-4"> <TabsContent value="evoluciones" className="mt-4">
<SeccionEvoluciones evos={evoluciones} internacionId={internacion.id} add={onAgregarEvolucion} update={onActualizarEvolucion} del={onEliminarEvolucion} /> <SeccionEvoluciones evos={evoluciones} internacionId={internacion.id} add={onAgregarEvolucion} update={onActualizarEvolucion} del={onEliminarEvolucion} canEdit={canEdit} />
</TabsContent> </TabsContent>
<TabsContent value="acidobase" className="mt-4"> <TabsContent value="acidobase" className="mt-4">
<SeccionAcidosBase ab={acidosBase} patientId={paciente.id} add={onAgregarAcidoBase} update={onActualizarAcidoBase} del={onEliminarAcidoBase} /> <SeccionAcidosBase ab={acidosBase} patientId={paciente.id} add={onAgregarAcidoBase} update={onActualizarAcidoBase} del={onEliminarAcidoBase} canEdit={canEdit} />
</TabsContent> </TabsContent>
<TabsContent value="cultivos" className="mt-4"> <TabsContent value="cultivos" className="mt-4">
<SeccionCultivos cults={cultivos} patient={paciente} add={onAgregarCultivo} update={onActualizarCultivo} del={onEliminarCultivo} /> <SeccionCultivos cults={cultivos} patient={paciente} add={onAgregarCultivo} update={onActualizarCultivo} del={onEliminarCultivo} canEdit={canEdit} />
</TabsContent> </TabsContent>
<TabsContent value="atb" className="mt-4"> <TabsContent value="atb" className="mt-4">
@@ -558,6 +556,7 @@ export function HistoriaClinica({
add={onAgregarATB} add={onAgregarATB}
update={onActualizarATB} update={onActualizarATB}
del={onEliminarATB} del={onEliminarATB}
canEdit={canEdit}
/> />
</TabsContent> </TabsContent>
@@ -570,6 +569,7 @@ export function HistoriaClinica({
del={onEliminarIndicacion} del={onEliminarIndicacion}
movimientos={movimientos} movimientos={movimientos}
onAgregarMovimiento={onAgregarMovimiento} onAgregarMovimiento={onAgregarMovimiento}
canEdit={canEdit}
/> />
</TabsContent> </TabsContent>
@@ -581,6 +581,7 @@ export function HistoriaClinica({
add={onAgregarEstudioComplementario} add={onAgregarEstudioComplementario}
update={onActualizarEstudioComplementario} update={onActualizarEstudioComplementario}
del={onEliminarEstudioComplementario} del={onEliminarEstudioComplementario}
canEdit={canEdit}
/> />
</TabsContent> </TabsContent>
@@ -592,6 +593,7 @@ export function HistoriaClinica({
add={onAgregarInterconsulta} add={onAgregarInterconsulta}
update={onActualizarInterconsulta} update={onActualizarInterconsulta}
del={onEliminarInterconsulta} del={onEliminarInterconsulta}
canEdit={canEdit}
/> />
</TabsContent> </TabsContent>
</Tabs> </Tabs>
@@ -601,13 +603,14 @@ export function HistoriaClinica({
); );
} }
function SeccionLaboratorios({ lab, patientId, add, update, del, addAcidoBase }: { function SeccionLaboratorios({ lab, patientId, add, update, del, addAcidoBase, canEdit }: {
lab: Laboratorio[]; lab: Laboratorio[];
patientId: string; patientId: string;
add: (l: Omit<Laboratorio, 'id'>) => void; add: (l: Omit<Laboratorio, 'id'>) => void;
update: (id: string, data: Partial<Laboratorio>) => void; update: (id: string, data: Partial<Laboratorio>) => void;
del: (id: string) => void; del: (id: string) => void;
addAcidoBase?: (a: Omit<AcidoBase, 'id'>) => void; addAcidoBase?: (a: Omit<AcidoBase, 'id'>) => void;
canEdit?: boolean;
}) { }) {
const [dialog, setDialog] = useState(false); const [dialog, setDialog] = useState(false);
const [obsDialog, setObsDialog] = useState(false); const [obsDialog, setObsDialog] = useState(false);
@@ -954,7 +957,7 @@ function SeccionLaboratorios({ lab, patientId, add, update, del, addAcidoBase }:
<div className="space-y-4 w-full max-w-full min-w-0"> <div className="space-y-4 w-full max-w-full min-w-0">
<div className="flex justify-between"> <div className="flex justify-between">
<div className="flex gap-2"> <div className="flex gap-2">
<Button variant="outline" onClick={() => { reset(); setDialog(true); }}><Plus className="h-4 w-4 mr-2" />Nuevo</Button> {canEdit && <Button variant="outline" onClick={() => { reset(); setDialog(true); }}><Plus className="h-4 w-4 mr-2" />Nuevo</Button>}
<Button variant="outline" onClick={() => { setSelectedLab(lab[0] || null); setParametroEvolucion('Hematocrito'); setEvolDialog(true); }}><TrendingUp className="h-4 w-4 mr-2" />Evolución</Button> <Button variant="outline" onClick={() => { setSelectedLab(lab[0] || null); setParametroEvolucion('Hematocrito'); setEvolDialog(true); }}><TrendingUp className="h-4 w-4 mr-2" />Evolución</Button>
<Button variant="outline" onClick={() => { setImportFecha(new Date().toISOString().split('T')[0]); setImportHora(new Date().toTimeString().slice(0, 5)); setImportTexto(''); setImportResultados([]); setImportObservaciones(''); setImportDialog(true); }}><FileText className="h-4 w-4 mr-2" />Importar</Button> <Button variant="outline" onClick={() => { setImportFecha(new Date().toISOString().split('T')[0]); setImportHora(new Date().toTimeString().slice(0, 5)); setImportTexto(''); setImportResultados([]); setImportObservaciones(''); setImportDialog(true); }}><FileText className="h-4 w-4 mr-2" />Importar</Button>
</div> </div>
@@ -1279,12 +1282,13 @@ function SeccionLaboratorios({ lab, patientId, add, update, del, addAcidoBase }:
); );
} }
function SeccionEvoluciones({ evos, internacionId, add, update, del }: { function SeccionEvoluciones({ evos, internacionId, add, update, del, canEdit }: {
evos: Evolucion[]; evos: Evolucion[];
internacionId: string; internacionId: string;
add: (e: Omit<Evolucion, 'id'>) => void; add: (e: Omit<Evolucion, 'id'>) => void;
update: (id: string, data: Partial<Evolucion>) => void; update: (id: string, data: Partial<Evolucion>) => void;
del: (id: string) => void; del: (id: string) => void;
canEdit?: boolean;
}) { }) {
const [dialog, setDialog] = useState(false); const [dialog, setDialog] = useState(false);
const [edit, setEdit] = useState<Evolucion | null>(null); const [edit, setEdit] = useState<Evolucion | null>(null);
@@ -1376,7 +1380,7 @@ function SeccionEvoluciones({ evos, internacionId, add, update, del }: {
return ( return (
<div className="space-y-4"> <div className="space-y-4">
<div className="flex"> <div className="flex">
<Button variant="outline" onClick={() => { reset(); setDialog(true); }}><Plus className="h-4 w-4 mr-2" />Nueva Evolución</Button> {canEdit && <Button variant="outline" onClick={() => { reset(); setDialog(true); }}><Plus className="h-4 w-4 mr-2" />Nueva Evolución</Button>}
</div> </div>
<Dialog open={dialog} onOpenChange={setDialog}> <Dialog open={dialog} onOpenChange={setDialog}>
<DialogContent className="sm:max-w-3xl max-h-[90vh] overflow-y-auto"> <DialogContent className="sm:max-w-3xl max-h-[90vh] overflow-y-auto">
@@ -1431,8 +1435,8 @@ function SeccionEvoluciones({ evos, internacionId, add, update, del }: {
<div><p className="font-medium">{e.fecha} {e.hora}</p><p className="text-sm text-gray-600">Médico: {e.medico}</p></div> <div><p className="font-medium">{e.fecha} {e.hora}</p><p className="text-sm text-gray-600">Médico: {e.medico}</p></div>
<div className="flex gap-1"> <div className="flex gap-1">
<Button size="sm" variant="outline" onClick={() => setExpanded(isExpanded ? null : e.id)}>{isExpanded ? <ChevronUp className="h-4 w-4" /> : <ChevronDown className="h-4 w-4" />}</Button> <Button size="sm" variant="outline" onClick={() => setExpanded(isExpanded ? null : e.id)}>{isExpanded ? <ChevronUp className="h-4 w-4" /> : <ChevronDown className="h-4 w-4" />}</Button>
<Button size="sm" variant="outline" onClick={() => openEdit(e)}><Pencil /></Button> {canEdit && <Button size="sm" variant="outline" onClick={() => openEdit(e)}><Pencil /></Button>}
<Button size="sm" variant="outline" className="text-red-600" onClick={() => del(e.id)}><Trash2 /></Button> {canEdit && <Button size="sm" variant="outline" className="text-red-600" onClick={() => del(e.id)}><Trash2 /></Button>}
</div> </div>
</div> </div>
{svText && <div className="bg-blue-50 p-2 rounded-lg"><p className="text-sm font-medium text-blue-800 flex items-center gap-2"><Activity className="h-4 w-4" />Signos Vitales: {svText}</p></div>} {svText && <div className="bg-blue-50 p-2 rounded-lg"><p className="text-sm font-medium text-blue-800 flex items-center gap-2"><Activity className="h-4 w-4" />Signos Vitales: {svText}</p></div>}
@@ -1459,12 +1463,13 @@ function SeccionEvoluciones({ evos, internacionId, add, update, del }: {
); );
} }
function SeccionAcidosBase({ ab, patientId, add, update, del }: { function SeccionAcidosBase({ ab, patientId, add, update, del, canEdit }: {
ab: AcidoBase[]; ab: AcidoBase[];
patientId: string; patientId: string;
add: (a: Omit<AcidoBase, 'id'>) => void; add: (a: Omit<AcidoBase, 'id'>) => void;
update: (id: string, datos: Partial<AcidoBase>) => void; update: (id: string, datos: Partial<AcidoBase>) => void;
del: (id: string) => void; del: (id: string) => void;
canEdit?: boolean;
}) { }) {
const [dialog, setDialog] = useState(false); const [dialog, setDialog] = useState(false);
const [editDialog, setEditDialog] = useState(false); const [editDialog, setEditDialog] = useState(false);
@@ -1552,7 +1557,7 @@ function SeccionAcidosBase({ ab, patientId, add, update, del }: {
return ( return (
<div className="space-y-4"> <div className="space-y-4">
<div className="flex"> <div className="flex">
<Button variant="outline" onClick={() => { reset(); setDialog(true); }}><Plus className="h-4 w-4 mr-2" />Nueva Gasometría</Button> {canEdit && <Button variant="outline" onClick={() => { reset(); setDialog(true); }}><Plus className="h-4 w-4 mr-2" />Nueva Gasometría</Button>}
</div> </div>
<Dialog open={dialog} onOpenChange={setDialog}> <Dialog open={dialog} onOpenChange={setDialog}>
<DialogContent className="sm:max-w-2xl max-h-[90vh] overflow-y-auto"> <DialogContent className="sm:max-w-2xl max-h-[90vh] overflow-y-auto">
@@ -1691,12 +1696,13 @@ function SeccionAcidosBase({ ab, patientId, add, update, del }: {
); );
} }
function SeccionCultivos({ cults, patient, add, update, del }: { function SeccionCultivos({ cults, patient, add, update, del, canEdit }: {
cults: Cultivo[]; cults: Cultivo[];
patient: Paciente; patient: Paciente;
add: (c: Omit<Cultivo, 'id'>) => void; add: (c: Omit<Cultivo, 'id'>) => void;
update: (id: string, data: Partial<Cultivo>) => void; update: (id: string, data: Partial<Cultivo>) => void;
del: (id: string) => void; del: (id: string) => void;
canEdit?: boolean;
}) { }) {
const [dialog, setDialog] = useState(false); const [dialog, setDialog] = useState(false);
const [resDialog, setResDialog] = useState(false); const [resDialog, setResDialog] = useState(false);
@@ -1811,7 +1817,7 @@ function SeccionCultivos({ cults, patient, add, update, del }: {
return ( return (
<div className="space-y-4"> <div className="space-y-4">
<div className="flex"> <div className="flex">
<Button variant="outline" onClick={() => { reset(); setDialog(true); }}><Plus className="h-4 w-4 mr-2" />Nuevo Cultivo</Button> {canEdit && <Button variant="outline" onClick={() => { reset(); setDialog(true); }}><Plus className="h-4 w-4 mr-2" />Nuevo Cultivo</Button>}
</div> </div>
<Dialog open={dialog} onOpenChange={setDialog}> <Dialog open={dialog} onOpenChange={setDialog}>
<DialogContent className="sm:max-w-lg"> <DialogContent className="sm:max-w-lg">
@@ -1966,13 +1972,14 @@ function SeccionCultivos({ cults, patient, add, update, del }: {
); );
} }
function SeccionEstudiosComplementarios({ estudios, internacionId, pacienteId, add, update, del }: { function SeccionEstudiosComplementarios({ estudios, internacionId, pacienteId, add, update, del, canEdit }: {
estudios: EstudioComplementario[]; estudios: EstudioComplementario[];
internacionId: string; internacionId: string;
pacienteId: string; pacienteId: string;
add: (e: Omit<EstudioComplementario, 'id'>) => void; add: (e: Omit<EstudioComplementario, 'id'>) => void;
update: (id: string, datos: Partial<EstudioComplementario>) => void; update: (id: string, datos: Partial<EstudioComplementario>) => void;
del: (id: string) => void; del: (id: string) => void;
canEdit?: boolean;
}) { }) {
const [dialog, setDialog] = useState(false); const [dialog, setDialog] = useState(false);
const [filtro, setFiltro] = useState<'todos' | 'internacion'>('internacion'); const [filtro, setFiltro] = useState<'todos' | 'internacion'>('internacion');
@@ -2021,10 +2028,10 @@ function SeccionEstudiosComplementarios({ estudios, internacionId, pacienteId, a
<div className="space-y-4"> <div className="space-y-4">
<div className="flex flex-col sm:flex-row justify-between items-start sm:items-center gap-4"> <div className="flex flex-col sm:flex-row justify-between items-start sm:items-center gap-4">
<div className="flex gap-2"> <div className="flex gap-2">
<Button variant="outline" onClick={() => { reset(); setDialog(true); }}> {canEdit && <Button variant="outline" onClick={() => { reset(); setDialog(true); }}>
<Plus className="h-4 w-4 mr-2" /> <Plus className="h-4 w-4 mr-2" />
Nuevo Estudio Nuevo Estudio
</Button> </Button>}
</div> </div>
<Select value={filtro} onValueChange={(v: 'todos' | 'internacion') => setFiltro(v)}> <Select value={filtro} onValueChange={(v: 'todos' | 'internacion') => setFiltro(v)}>
<SelectTrigger className="w-[180px]"> <SelectTrigger className="w-[180px]">
@@ -2095,12 +2102,14 @@ function SeccionEstudiosComplementarios({ estudios, internacionId, pacienteId, a
<p className="text-sm text-gray-600 dark:text-gray-300 mt-1 whitespace-pre-wrap">{e.resultado}</p> <p className="text-sm text-gray-600 dark:text-gray-300 mt-1 whitespace-pre-wrap">{e.resultado}</p>
</div> </div>
<div className="flex gap-1 self-start"> <div className="flex gap-1 self-start">
{canEdit && <>
<Button size="sm" variant="outline" onClick={() => loadEdit(e)}> <Button size="sm" variant="outline" onClick={() => loadEdit(e)}>
<Pencil className="h-4 w-4" /> <Pencil className="h-4 w-4" />
</Button> </Button>
<Button size="sm" variant="outline" className="text-red-600" onClick={() => del(e.id)}> <Button size="sm" variant="outline" className="text-red-600" onClick={() => del(e.id)}>
<Trash2 className="h-4 w-4" /> <Trash2 className="h-4 w-4" />
</Button> </Button>
</>}
</div> </div>
</div> </div>
</CardContent> </CardContent>
@@ -2112,13 +2121,14 @@ function SeccionEstudiosComplementarios({ estudios, internacionId, pacienteId, a
); );
} }
function SeccionInterconsultas({ interconsultas, pacienteId, internacionId, add, update, del }: { function SeccionInterconsultas({ interconsultas, pacienteId, internacionId, add, update, del, canEdit }: {
interconsultas: Interconsulta[]; interconsultas: Interconsulta[];
pacienteId: string; pacienteId: string;
internacionId: string; internacionId: string;
add: (ic: Omit<Interconsulta, 'id'>) => void; add: (ic: Omit<Interconsulta, 'id'>) => void;
update: (id: string, datos: Partial<Interconsulta>) => void; update: (id: string, datos: Partial<Interconsulta>) => void;
del: (id: string) => void; del: (id: string) => void;
canEdit?: boolean;
}) { }) {
const [dialog, setDialog] = useState(false); const [dialog, setDialog] = useState(false);
const [fecha, setFecha] = useState(new Date().toISOString().split('T')[0]); const [fecha, setFecha] = useState(new Date().toISOString().split('T')[0]);
@@ -2167,10 +2177,10 @@ function SeccionInterconsultas({ interconsultas, pacienteId, internacionId, add,
return ( return (
<div className="space-y-4"> <div className="space-y-4">
<div className="flex justify-between items-center"> <div className="flex justify-between items-center">
<Button variant="outline" onClick={() => { reset(); setDialog(true); }}> {canEdit && <Button variant="outline" onClick={() => { reset(); setDialog(true); }}>
<Plus className="h-4 w-4 mr-2" /> <Plus className="h-4 w-4 mr-2" />
Nueva IC Nueva IC
</Button> </Button>}
</div> </div>
<Dialog open={dialog} onOpenChange={setDialog}> <Dialog open={dialog} onOpenChange={setDialog}>
@@ -2265,12 +2275,14 @@ function SeccionInterconsultas({ interconsultas, pacienteId, internacionId, add,
)} )}
</div> </div>
<div className="flex gap-1 self-start"> <div className="flex gap-1 self-start">
{canEdit && <>
<Button size="sm" variant="outline" onClick={() => loadEdit(ic)}> <Button size="sm" variant="outline" onClick={() => loadEdit(ic)}>
<Pencil className="h-4 w-4" /> <Pencil className="h-4 w-4" />
</Button> </Button>
<Button size="sm" variant="outline" className="text-red-600" onClick={() => del(ic.id)}> <Button size="sm" variant="outline" className="text-red-600" onClick={() => del(ic.id)}>
<Trash2 className="h-4 w-4" /> <Trash2 className="h-4 w-4" />
</Button> </Button>
</>}
</div> </div>
</div> </div>
</CardContent> </CardContent>
@@ -2282,13 +2294,14 @@ function SeccionInterconsultas({ interconsultas, pacienteId, internacionId, add,
); );
} }
function SeccionATB({ atb, internacionId, pacienteId, add, update, del }: { function SeccionATB({ atb, internacionId, pacienteId, add, update, del, canEdit }: {
atb: ATB[]; atb: ATB[];
internacionId: string; internacionId: string;
pacienteId: string; pacienteId: string;
add: (a: Omit<ATB, 'id'>) => void; add: (a: Omit<ATB, 'id'>) => void;
update: (id: string, data: Partial<ATB>) => void; update: (id: string, data: Partial<ATB>) => void;
del: (id: string) => void; del: (id: string) => void;
canEdit?: boolean;
}) { }) {
const [dialog, setDialog] = useState(false); const [dialog, setDialog] = useState(false);
const [edit, setEdit] = useState<ATB | null>(null); const [edit, setEdit] = useState<ATB | null>(null);
@@ -2333,9 +2346,9 @@ function SeccionATB({ atb, internacionId, pacienteId, add, update, del }: {
return ( return (
<div className="space-y-4"> <div className="space-y-4">
<div className="flex justify-between"> <div className="flex justify-between">
<Button variant="outline" onClick={() => { reset(); setDialog(true); }}> {canEdit && <Button variant="outline" onClick={() => { reset(); setDialog(true); }}>
<Plus className="h-4 w-4 mr-2" />Nuevo <Plus className="h-4 w-4 mr-2" />Nuevo
</Button> </Button>}
</div> </div>
<Dialog open={dialog} onOpenChange={setDialog}> <Dialog open={dialog} onOpenChange={setDialog}>
@@ -2388,12 +2401,14 @@ function SeccionATB({ atb, internacionId, pacienteId, add, update, del }: {
</div> </div>
</div> </div>
<div className="flex gap-1"> <div className="flex gap-1">
{canEdit && <>
<Button size="sm" variant="outline" onClick={() => loadEdit(a)}> <Button size="sm" variant="outline" onClick={() => loadEdit(a)}>
<Pencil className="h-4 w-4" /> <Pencil className="h-4 w-4" />
</Button> </Button>
<Button size="sm" variant="outline" className="text-red-600" onClick={() => del(a.id)}> <Button size="sm" variant="outline" className="text-red-600" onClick={() => del(a.id)}>
<Trash2 className="h-4 w-4" /> <Trash2 className="h-4 w-4" />
</Button> </Button>
</>}
</div> </div>
</CardContent> </CardContent>
</Card> </Card>
+1
View File
@@ -20,6 +20,7 @@ export function Login() {
try { try {
await login(dni, password); await login(dni, password);
window.location.reload();
} catch (err: any) { } catch (err: any) {
setError(err.message || 'Error de autenticación'); setError(err.message || 'Error de autenticación');
} finally { } finally {
+10 -4
View File
@@ -9,6 +9,7 @@ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@
import { Label } from '@/components/ui/label'; import { Label } from '@/components/ui/label';
import { Input } from '@/components/ui/input'; import { Input } from '@/components/ui/input';
import type { Cama, Paciente, Internacion, Area } from '@/types'; import type { Cama, Paciente, Internacion, Area } from '@/types';
import { useHospitalStore } from '@/hooks/useHospitalStore';
interface MapaCamasProps { interface MapaCamasProps {
camas: Cama[]; camas: Cama[];
@@ -41,6 +42,7 @@ export function MapaCamas({
getPacienteById, getPacienteById,
getInternacionById getInternacionById
}: MapaCamasProps) { }: MapaCamasProps) {
const { canEditCama, canEditInArea } = useHospitalStore();
const [filtroSala, setFiltroSala] = useState<string>('todas'); const [filtroSala, setFiltroSala] = useState<string>('todas');
const [filtroTipo, setFiltroTipo] = useState<string>('todos'); const [filtroTipo, setFiltroTipo] = useState<string>('todos');
const [filtroEstado, setFiltroEstado] = useState<string>('todos'); const [filtroEstado, setFiltroEstado] = useState<string>('todos');
@@ -324,7 +326,7 @@ export function MapaCamas({
<div className="space-y-2"> <div className="space-y-2">
<p className="font-medium">Acciones:</p> <p className="font-medium">Acciones:</p>
<div className="grid grid-cols-1 sm:grid-cols-2 gap-2"> <div className="grid grid-cols-1 sm:grid-cols-2 gap-2">
{cama.estado === 'Disponible' && ( {cama.estado === 'Disponible' && canEditCama(cama.id) && (
<Dialog open={dialogoAbierto} onOpenChange={setDialogoAbierto}> <Dialog open={dialogoAbierto} onOpenChange={setDialogoAbierto}>
<DialogTrigger asChild> <DialogTrigger asChild>
<Button variant="outline" <Button variant="outline"
@@ -411,7 +413,7 @@ export function MapaCamas({
</Dialog> </Dialog>
)} )}
{cama.estado !== 'Disponible' && ( {cama.estado !== 'Disponible' && canEditCama(cama.id) && (
<Button <Button
variant="outline" variant="outline"
className="w-full" className="w-full"
@@ -422,7 +424,7 @@ export function MapaCamas({
</Button> </Button>
)} )}
{cama.estado !== 'Reparacion' && ( {cama.estado !== 'Reparacion' && canEditCama(cama.id) && (
<Button <Button
variant="outline" variant="outline"
className="w-full" className="w-full"
@@ -433,7 +435,7 @@ export function MapaCamas({
</Button> </Button>
)} )}
{cama.estado !== 'Reservada' && ( {cama.estado !== 'Reservada' && canEditCama(cama.id) && (
<Button <Button
variant="outline" variant="outline"
className="w-full" className="w-full"
@@ -445,6 +447,7 @@ export function MapaCamas({
)} )}
{canEditCama(cama.id) && (
<Button variant="outline" onClick={() => { <Button variant="outline" onClick={() => {
setEditingBed(cama); setEditingBed(cama);
setBedNumero(cama.numero); setBedNumero(cama.numero);
@@ -454,6 +457,8 @@ export function MapaCamas({
}}> }}>
<Edit2 className="h-4 w-4 mr-1" />Editar <Edit2 className="h-4 w-4 mr-1" />Editar
</Button> </Button>
)}
{canEditCama(cama.id) && (
<Button variant="destructive" onClick={() => { <Button variant="destructive" onClick={() => {
if (confirm('Eliminar esta cama?')) { if (confirm('Eliminar esta cama?')) {
onEliminarCama(cama.id); onEliminarCama(cama.id);
@@ -461,6 +466,7 @@ export function MapaCamas({
}}> }}>
<Trash className="h-4 w-4 mr-1" />Eliminar <Trash className="h-4 w-4 mr-1" />Eliminar
</Button> </Button>
)}
</div> </div>
</div> </div>
</div> </div>
+8 -7
View File
@@ -10,7 +10,7 @@ import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@
import { Plus, Pencil, Trash2, X } from 'lucide-react'; import { Plus, Pencil, Trash2, X } from 'lucide-react';
import type { Indicacion, IndicacionTipo, ViaAdministracion, TipoPlanHidratacion, TipoInsulina } from '@/types'; import type { Indicacion, IndicacionTipo, ViaAdministracion, TipoPlanHidratacion, TipoInsulina } from '@/types';
export function SeccionIndicaciones({ recomendaciones, internacionId, add, update, del, movimientos, onAgregarMovimiento }: { export function SeccionIndicaciones({ recomendaciones, internacionId, add, update, del, movimientos, onAgregarMovimiento, canEdit }: {
recomendaciones: Indicacion[]; recomendaciones: Indicacion[];
internacionId: string; internacionId: string;
add: (i: Omit<Indicacion, 'id'>) => void; add: (i: Omit<Indicacion, 'id'>) => void;
@@ -18,6 +18,7 @@ export function SeccionIndicaciones({ recomendaciones, internacionId, add, updat
del: (id: string) => void; del: (id: string) => void;
movimientos?: any[]; movimientos?: any[];
onAgregarMovimiento?: (m: any) => void; onAgregarMovimiento?: (m: any) => void;
canEdit?: boolean;
}) { }) {
const list: Indicacion[] = Array.isArray(recomendaciones) ? recomendaciones : []; const list: Indicacion[] = Array.isArray(recomendaciones) ? recomendaciones : [];
const movs: any[] = Array.isArray(movimientos) ? movimientos : []; const movs: any[] = Array.isArray(movimientos) ? movimientos : [];
@@ -218,9 +219,9 @@ export function SeccionIndicaciones({ recomendaciones, internacionId, add, updat
return ( return (
<div className="space-y-4"> <div className="space-y-4">
<div className="flex justify-between"> <div className="flex justify-between">
<Button variant="outline" onClick={() => { reset(); setDialog(true); }}> {canEdit && <Button variant="outline" onClick={() => { reset(); setDialog(true); }}>
<Plus className="h-4 w-4 mr-2" />Nueva Indicación <Plus className="h-4 w-4 mr-2" />Nueva Indicación
</Button> </Button>}
<Button variant="ghost" onClick={() => setShowHistorial(!showHistorial)}> <Button variant="ghost" onClick={() => setShowHistorial(!showHistorial)}>
{showHistorial ? 'Ocultar Historial' : `Ver Historial (${movs.length})`} {showHistorial ? 'Ocultar Historial' : `Ver Historial (${movs.length})`}
</Button> </Button>
@@ -355,12 +356,12 @@ export function SeccionIndicaciones({ recomendaciones, internacionId, add, updat
}}> }}>
<X className="h-4 w-4" /> <X className="h-4 w-4" />
</Button> </Button>
<Button size="sm" variant="outline" onClick={() => loadEdit(ind)}> {canEdit && <Button size="sm" variant="outline" onClick={() => loadEdit(ind)}>
<Pencil className="h-4 w-4" /> <Pencil className="h-4 w-4" />
</Button> </Button>}
<Button size="sm" variant="outline" className="text-red-600" onClick={() => del(ind.id)}> {canEdit && <Button size="sm" variant="outline" className="text-red-600" onClick={() => del(ind.id)}>
<Trash2 className="h-4 w-4" /> <Trash2 className="h-4 w-4" />
</Button> </Button>}
</div> </div>
</CardContent> </CardContent>
</Card> </Card>