feat: 6 nuevas funcionalidades - notas obligatorias turno, reporte enfermero, plantilla WhatsApp, adjuntar orden modal, roles solo lectura, fix valores copago
This commit is contained in:
+104
-13
@@ -10,9 +10,10 @@ if (!isUserLoggedIn()) {
|
||||
header('Location: login.php'); exit;
|
||||
}
|
||||
|
||||
$usuario = $_SESSION['admin_user'];
|
||||
$rol = $usuario['role'] ?? 'admin';
|
||||
$enfId = (int)($usuario['enfermera_id'] ?? 0);
|
||||
$usuario = $_SESSION['admin_user'];
|
||||
$rol = $usuario['role'] ?? 'admin';
|
||||
$enfId = (int)($usuario['enfermera_id'] ?? 0);
|
||||
$enfermeraNombre = $usuario['full_name'] ?? $usuario['username'] ?? 'El profesional de salud';
|
||||
|
||||
// Si es admin puede simular ver la agenda de otra enfermera vía ?eid=X
|
||||
if ($rol === 'admin' && isset($_GET['eid'])) {
|
||||
@@ -386,8 +387,9 @@ $hoy = date('Y-m-d');
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<script>
|
||||
const ENFERMERA_ID = <?= (int)$enfId ?>;
|
||||
const ROL = '<?= $rol ?>';
|
||||
const ENFERMERA_ID = <?= (int)$enfId ?>;
|
||||
const ROL = '<?= $rol ?>';
|
||||
const ENFERMERO_NOMBRE = <?= json_encode($enfermeraNombre) ?>;
|
||||
|
||||
// Etiquetas descriptivas
|
||||
const ESTADOS_LABEL = {
|
||||
@@ -662,10 +664,18 @@ const portal = {
|
||||
<i class="fas fa-plus me-1"></i>Servicio extra
|
||||
</button>` : ''}
|
||||
${item.paciente_telefono
|
||||
? `<a class="btn btn-outline-success btn-accion"
|
||||
href="https://wa.me/${item.paciente_telefono.replace(/\D/g,'')}" target="_blank">
|
||||
<i class="fab fa-whatsapp"></i>
|
||||
</a>` : ''}
|
||||
? (()=>{
|
||||
const tel = item.paciente_telefono.replace(/\D/g,'');
|
||||
const txt = encodeURIComponent(
|
||||
'Hola, soy ' + ENFERMERO_NOMBRE + ', el profesional de salud asignado a su atenci\u00f3n. '+
|
||||
'Me comunico con ' + (item.paciente_nombre||'usted') + ' para confirmar la visita programada. \u00bfTiene alguna pregunta?'
|
||||
);
|
||||
return `<a class="btn btn-outline-success btn-accion"
|
||||
href="https://wa.me/${tel}?text=${txt}" target="_blank"
|
||||
title="Enviar mensaje WhatsApp">
|
||||
<i class="fab fa-whatsapp"></i>
|
||||
</a>`;
|
||||
})() : ''}
|
||||
<button class="btn btn-outline-secondary btn-accion btn-form-envio"
|
||||
style="color:#6f42c1;border-color:#6f42c1"
|
||||
data-dom-id="${item.domicilio_id}"
|
||||
@@ -686,6 +696,43 @@ const portal = {
|
||||
|
||||
// ── Cambiar estado ─────────────────────────────────────────────────────
|
||||
async actualizarEstado(domId, nuevoEstado, notas = '') {
|
||||
// ── BLOQUEO: para completar es obligatorio tener al menos una nota ──
|
||||
if (nuevoEstado === 'completado') {
|
||||
// Verificar en caché o cargar desde API
|
||||
let cache = notasManager._cache[domId];
|
||||
if (!cache) {
|
||||
try {
|
||||
const r = await fetch(`api/lab/get_notas_domicilio.php?domicilio_id=${domId}`);
|
||||
const d = await r.json();
|
||||
const notas = d.success ? (d.data || []) : [];
|
||||
cache = {
|
||||
clinica: notas.find(n => n.tipo === 'clinica') || null,
|
||||
libres: notas.filter(n => n.tipo === 'libre')
|
||||
};
|
||||
notasManager._cache[domId] = cache;
|
||||
} catch (e) {
|
||||
cache = { clinica: null, libres: [] };
|
||||
}
|
||||
}
|
||||
const tieneNota = cache.clinica || cache.libres.length > 0;
|
||||
if (!tieneNota) {
|
||||
// Abrir el sheet de notas para que registre una antes de completar
|
||||
// Guardamos el intento para retomarlo al cerrar la nota
|
||||
notasManager._pendingCompleteId = domId;
|
||||
// Mostrar aviso visual dentro del sheet
|
||||
notasManager.abrirLibre(domId, null);
|
||||
// Colocar banner de advertencia en la parte superior del sheet
|
||||
const inner = document.getElementById('nota-sheet-inner');
|
||||
if (inner) {
|
||||
const banner = document.createElement('div');
|
||||
banner.id = 'nota-required-banner';
|
||||
banner.style.cssText = 'background:#fff3cd;border:1px solid #ffc107;border-radius:8px;padding:10px 12px;margin-bottom:12px;font-size:.82rem;color:#856404;';
|
||||
banner.innerHTML = '<i class="fas fa-exclamation-triangle me-1"></i><strong>Nota obligatoria</strong> — Debes registrar al menos una nota antes de marcar este domicilio como completado.';
|
||||
inner.prepend(banner);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
try {
|
||||
const r = await fetch('api/lab/update_domicilio_enfermero.php', {
|
||||
method: 'POST',
|
||||
@@ -1100,6 +1147,16 @@ const formVer = {
|
||||
placeholder="Instrucciones adicionales para el servicio…"></textarea>
|
||||
</div>
|
||||
|
||||
<!-- Orden médica / Archivo -->
|
||||
<div class="mb-2">
|
||||
<label class="form-label small fw-semibold">
|
||||
Orden médica / Archivo <span class="text-muted fw-normal">(opcional)</span>
|
||||
</label>
|
||||
<input type="file" id="na-orden-file" accept=".pdf,.jpg,.jpeg,.png"
|
||||
class="form-control form-control-sm">
|
||||
<div id="na-orden-preview" class="mt-1 small text-muted"></div>
|
||||
</div>
|
||||
|
||||
<div id="na-error" class="alert alert-danger py-2 d-none small"></div>
|
||||
</div>
|
||||
<div class="modal-footer py-2">
|
||||
@@ -1386,6 +1443,23 @@ const agendaNueva = {
|
||||
});
|
||||
const d = await r.json();
|
||||
if (!d.success) throw new Error(d.error || 'Error al guardar');
|
||||
|
||||
// Subir archivo adjunto si el usuario seleccionó uno
|
||||
const archivoInput = document.getElementById('na-orden-file');
|
||||
if (archivoInput?.files?.length && d.id) {
|
||||
const fd = new FormData();
|
||||
fd.append('file', archivoInput.files[0]);
|
||||
fd.append('domicilio_id', d.id);
|
||||
try {
|
||||
const rFile = await fetch('api/lab/upload_orden.php', { method: 'POST', body: fd });
|
||||
// Si falla el upload no bloqueamos; el domicilio ya fue creado
|
||||
const dFile = await rFile.json();
|
||||
if (!dFile.success) console.warn('No se pudo adjuntar el archivo:', dFile.error);
|
||||
} catch (eFile) {
|
||||
console.warn('Error al subir archivo:', eFile);
|
||||
}
|
||||
}
|
||||
|
||||
this._modal.hide();
|
||||
portal.cargar();
|
||||
} catch (e) {
|
||||
@@ -1620,6 +1694,7 @@ const notasManager = {
|
||||
_domId: null, // domicilio actualmente en el sheet
|
||||
_notaId: null, // null = nueva nota libre, número = editar
|
||||
_imagen: null, // File pendiente de subir
|
||||
_pendingCompleteId: null, // domicilio_id pendiente de marcar completado tras guardar nota
|
||||
|
||||
// ── Abrir/cerrar sección de notas en la tarjeta ────────────────────────
|
||||
async toggle(domId, btn) {
|
||||
@@ -1847,9 +1922,17 @@ const notasManager = {
|
||||
});
|
||||
const d = await r.json();
|
||||
if (!d.success) throw new Error(d.error || 'Error al guardar');
|
||||
delete this._cache[this._domId];
|
||||
const savedDomId = this._domId;
|
||||
delete this._cache[savedDomId];
|
||||
this._cerrarSheet();
|
||||
await this._renderSeccion(this._domId);
|
||||
await this._renderSeccion(savedDomId);
|
||||
// Si había un completado pendiente para este domicilio, retomarlo
|
||||
if (this._pendingCompleteId && this._pendingCompleteId == savedDomId) {
|
||||
this._pendingCompleteId = null;
|
||||
if (confirm('✅ Nota guardada. ¿Marcar el domicilio como completado ahora?')) {
|
||||
portal.actualizarEstado(savedDomId, 'completado');
|
||||
}
|
||||
}
|
||||
} catch(e) { alert('Error: ' + e.message); }
|
||||
},
|
||||
|
||||
@@ -1886,9 +1969,17 @@ const notasManager = {
|
||||
});
|
||||
const d = await r.json();
|
||||
if (!d.success) throw new Error(d.error || 'Error al guardar');
|
||||
delete this._cache[this._domId];
|
||||
const savedDomId = this._domId;
|
||||
delete this._cache[savedDomId];
|
||||
this._cerrarSheet();
|
||||
await this._renderSeccion(this._domId);
|
||||
await this._renderSeccion(savedDomId);
|
||||
// Si había un completado pendiente para este domicilio, retomarlo
|
||||
if (this._pendingCompleteId && this._pendingCompleteId == savedDomId) {
|
||||
this._pendingCompleteId = null;
|
||||
if (confirm('✅ Nota guardada. ¿Marcar el domicilio como completado ahora?')) {
|
||||
portal.actualizarEstado(savedDomId, 'completado');
|
||||
}
|
||||
}
|
||||
} catch(e) { alert('Error: ' + e.message); }
|
||||
},
|
||||
|
||||
|
||||
Reference in New Issue
Block a user