- database/07_add_pago_domicilio.sql: 6 columnas nuevas en lab_domicilios (pago_estado ENUM, pago_modo, pago_monto, pago_fecha, pago_notas, pago_registrado_por) - Domicilio::filtrarCampos(): permite guardar campos de pago vía actualizar() - Enfermera::agenda(): incluye valor_domicilio, valor_copago, pago_estado, pago_modo, pago_monto, pago_notas - api/lab/registrar_pago.php: endpoint POST para registrar/actualizar pago del domicilio - enfermero_portal.php: bloque cobro en card, modal registrar pago (efectivo/transferencia/exento), objeto pagoModal JS - lab_domicilios.php: badge pago ⏳/✅/🔵 en fila tabla, sección pago detallada en panel lateral
548 lines
30 KiB
PHP
548 lines
30 KiB
PHP
<?php
|
|
/**
|
|
* Gestión de Domicilios — Módulo Administrativo de Laboratorio
|
|
*/
|
|
require_once 'config/config.php';
|
|
|
|
if (!isUserLoggedIn()) {
|
|
header('Location: login.php');
|
|
exit;
|
|
}
|
|
if (isEnfermero()) { header('Location: enfermero_portal.php'); exit; }
|
|
$adminNombre = $_SESSION['admin_user']['full_name'] ?? $_SESSION['admin_user']['username'] ?? 'Admin';
|
|
$ordenIdParam = (int)($_GET['orden_id'] ?? 0);
|
|
$pacIdParam = (int)($_GET['paciente_id']?? 0);
|
|
$domIdParam = (int)($_GET['id'] ?? 0);
|
|
?>
|
|
<!DOCTYPE html>
|
|
<html lang="es">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Domicilios — Módulo Lab</title>
|
|
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" rel="stylesheet">
|
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
|
<link href="./assets/css/styles.css?v=12" rel="stylesheet">
|
|
<style>
|
|
.dom-row { cursor:pointer; }
|
|
.estado-badge { font-size:.72rem; }
|
|
.agenda-card { border-left: 4px solid #dee2e6; padding: 8px 12px; margin-bottom: 8px; border-radius: 0 8px 8px 0; }
|
|
.agenda-card.programado { border-left-color: #6c757d; }
|
|
.agenda-card.en_camino { border-left-color: #0d6efd; }
|
|
.agenda-card.completado { border-left-color: #198754; }
|
|
.agenda-card.cancelado { border-left-color: #dc3545; }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
|
|
<nav class="sidebar">
|
|
<div class="sidebar-header"><h4><i class="fas fa-flask"></i> Módulo Lab</h4></div>
|
|
<ul class="sidebar-menu">
|
|
<li><a href="lab_dashboard.php" class="nav-link"><i class="fas fa-tachometer-alt"></i> Dashboard</a></li>
|
|
<li><a href="lab_ordenes.php" class="nav-link"><i class="fas fa-file-medical"></i> Órdenes</a></li>
|
|
<li><a href="lab_pacientes.php" class="nav-link"><i class="fas fa-users"></i> Pacientes</a></li>
|
|
<li><a href="lab_domicilios.php" class="nav-link active"><i class="fas fa-house-medical"></i> Domicilios</a></li>
|
|
<li><a href="lab_enfermeras.php" class="nav-link"><i class="fas fa-user-nurse"></i> Enfermeras</a></li>
|
|
<li><a href="lab_formularios.php" class="nav-link"><i class="fas fa-wpforms"></i> Formularios</a></li>
|
|
<li><a href="lab_reportes.php" class="nav-link"><i class="fas fa-chart-bar"></i> Reportes</a></li>
|
|
<li><a href="lab_configuracion.php" class="nav-link"><i class="fas fa-sliders-h"></i> Configuración</a></li>
|
|
<li><a href="lab_usuarios.php" class="nav-link"><i class="fas fa-users-cog"></i> Usuarios</a></li>
|
|
<li class="sidebar-divider"></li>
|
|
<li><a href="index.php" class="nav-link"><i class="fas fa-arrow-left"></i> Volver al Bot</a></li>
|
|
<li><a href="logout.php" class="nav-link logout-link" onclick="return confirm('¿Cerrar sesión?')"><i class="fas fa-sign-out-alt"></i> Cerrar Sesión</a></li>
|
|
</ul>
|
|
</nav>
|
|
|
|
<main class="main-content">
|
|
<header class="content-header d-flex align-items-center justify-content-between">
|
|
<div>
|
|
<h1><i class="fas fa-house-medical text-primary"></i> Domicilios</h1>
|
|
<small class="text-muted"><?= htmlspecialchars($adminNombre) ?></small>
|
|
</div>
|
|
<button class="btn btn-primary btn-sm" onclick="abrirFormulario()">
|
|
<i class="fas fa-plus me-1"></i> Nuevo Domicilio
|
|
</button>
|
|
</header>
|
|
|
|
<div class="container-fluid py-3">
|
|
<!-- Filtros -->
|
|
<div class="row g-2 mb-3">
|
|
<div class="col-md-2">
|
|
<input type="date" id="filtro-fecha" class="form-control form-control-sm"
|
|
value="<?= date('Y-m-d') ?>" onchange="cargarLista()">
|
|
</div>
|
|
<div class="col-md-2">
|
|
<select id="filtro-estado" class="form-select form-select-sm" onchange="cargarLista()">
|
|
<option value="">Todos los estados</option>
|
|
<option>programado</option><option>confirmado</option><option>en_camino</option>
|
|
<option>en_domicilio</option><option>completado</option><option>cancelado</option>
|
|
</select>
|
|
</div>
|
|
<div class="col-md-2">
|
|
<select id="filtro-enfermera" class="form-select form-select-sm" onchange="cargarLista()">
|
|
<option value="">Todas las enfermeras</option>
|
|
</select>
|
|
</div>
|
|
<div class="col-md-2">
|
|
<button class="btn btn-sm btn-secondary w-100" onclick="verHoy()">
|
|
<i class="fas fa-calendar-day me-1"></i> Hoy
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Resumen del día -->
|
|
<div class="row g-2 mb-3" id="resumen-hoy"></div>
|
|
|
|
<div class="row g-3">
|
|
<!-- Tabla -->
|
|
<div class="col-lg-5">
|
|
<div class="card border-0 shadow-sm">
|
|
<div class="card-body p-0">
|
|
<div class="table-responsive">
|
|
<table class="table table-hover mb-0">
|
|
<thead class="table-light">
|
|
<tr><th>Hora</th><th>Paciente</th><th>Enfermera</th><th>Estado</th></tr>
|
|
</thead>
|
|
<tbody id="tabla-body">
|
|
<tr><td colspan="4" class="text-center py-4 text-muted"><i class="fas fa-spinner fa-spin"></i></td></tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
<div class="d-flex align-items-center justify-content-between px-3 py-2 border-top" id="paginacion"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Detalle -->
|
|
<div class="col-lg-7">
|
|
<div class="card border-0 shadow-sm">
|
|
<div class="card-header bg-white border-0 d-flex justify-content-between align-items-center">
|
|
<h6 class="mb-0 fw-semibold">Detalle <span id="detail-id" class="text-muted"></span></h6>
|
|
<button class="btn-close" onclick="cerrarDetalle()"></button>
|
|
</div>
|
|
<div class="card-body" id="detail-body">
|
|
<div class="text-center py-5 text-muted">
|
|
<i class="fas fa-arrow-left me-2"></i>Selecciona un domicilio
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</main>
|
|
|
|
<!-- Modal Formulario -->
|
|
<div class="modal fade" id="modalDomicilio" tabindex="-1">
|
|
<div class="modal-dialog modal-lg">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title"><i class="fas fa-house-medical me-2"></i><span id="modal-titulo">Nuevo Domicilio</span></h5>
|
|
<button class="btn-close" data-bs-dismiss="modal"></button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<form id="form-domicilio">
|
|
<input type="hidden" name="id" id="dom-id">
|
|
<input type="hidden" name="orden_id" id="dom-orden-id" value="<?= $ordenIdParam ?>">
|
|
<div class="row g-3">
|
|
<div class="col-md-8">
|
|
<label class="form-label">Paciente <span class="text-danger">*</span></label>
|
|
<input type="hidden" name="paciente_id" id="dom-paciente-id" value="<?= $pacIdParam ?>">
|
|
<input type="text" class="form-control" id="dom-paciente-nombre" placeholder="Buscar paciente…" autocomplete="off">
|
|
<div class="list-group mt-1 shadow-sm" id="pac-suggest" style="position:absolute;z-index:1000;max-height:200px;overflow-y:auto;display:none"></div>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<label class="form-label">Tipo de servicio</label>
|
|
<input type="text" class="form-control" name="tipo_servicio" id="dom-tipo">
|
|
</div>
|
|
<div class="col-md-12">
|
|
<label class="form-label">Dirección <span class="text-danger">*</span></label>
|
|
<input type="text" class="form-control" name="direccion" id="dom-dir" required>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<label class="form-label">Ciudad</label>
|
|
<input type="text" class="form-control" name="ciudad" id="dom-ciudad">
|
|
</div>
|
|
<div class="col-md-4">
|
|
<label class="form-label">Barrio</label>
|
|
<input type="text" class="form-control" name="barrio" id="dom-barrio">
|
|
</div>
|
|
<div class="col-md-4">
|
|
<label class="form-label">Indicaciones de la dirección</label>
|
|
<input type="text" class="form-control" name="indicaciones_dir" id="dom-indic" placeholder="Ej: apto 302, tocar campanilla">
|
|
</div>
|
|
<div class="col-md-4">
|
|
<label class="form-label">Fecha programada <span class="text-danger">*</span></label>
|
|
<input type="date" class="form-control" name="fecha_programada" id="dom-fecha" required>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<label class="form-label">Hora</label>
|
|
<input type="time" class="form-control" name="hora_programada" id="dom-hora">
|
|
</div>
|
|
<div class="col-12">
|
|
<label class="form-label">Notas</label>
|
|
<textarea class="form-control" name="notas_admin" id="dom-notas" rows="2"></textarea>
|
|
</div>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button class="btn btn-secondary" data-bs-dismiss="modal">Cancelar</button>
|
|
<button class="btn btn-primary" onclick="guardarDomicilio()"><i class="fas fa-save me-1"></i> Guardar</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
|
<script>
|
|
const modal = new bootstrap.Modal('#modalDomicilio');
|
|
const COLOR_DOM = { programado:'secondary', confirmado:'info', en_camino:'primary', en_domicilio:'warning', completado:'success', cancelado:'danger', reprogramado:'dark' };
|
|
let domSeleccionado = <?= $domIdParam ?: 'null' ?>;
|
|
let paginaActual = 1;
|
|
let _listaToken = 0; // anti race-condition
|
|
|
|
// ── Init ───────────────────────────────────────────────────────────────────
|
|
(async function init() {
|
|
await cargarEnfermeras();
|
|
await cargarLista();
|
|
if (domSeleccionado) verDomicilio(domSeleccionado);
|
|
if (<?= $ordenIdParam ?> && <?= $pacIdParam ?>) abrirFormulario();
|
|
})();
|
|
|
|
async function cargarEnfermeras() {
|
|
const r = await fetch('api/lab/get_enfermeras.php');
|
|
const d = await r.json();
|
|
const sel = document.getElementById('filtro-enfermera');
|
|
(d.data||[]).forEach(e => {
|
|
sel.insertAdjacentHTML('beforeend', `<option value="${e.id}">${esc(e.nombre_completo)}</option>`);
|
|
});
|
|
}
|
|
|
|
// ── Lista ──────────────────────────────────────────────────────────────────
|
|
async function cargarLista(pag = 1) {
|
|
paginaActual = pag;
|
|
const miToken = ++_listaToken; // capturar token antes del await
|
|
const params = new URLSearchParams({
|
|
fecha: document.getElementById('filtro-fecha').value || '',
|
|
estado: document.getElementById('filtro-estado').value,
|
|
enfermera_id: document.getElementById('filtro-enfermera').value,
|
|
page: pag, limit: 25,
|
|
});
|
|
|
|
const r = await fetch(`api/lab/get_domicilios.php?${params}`);
|
|
const d = await r.json();
|
|
|
|
if (miToken !== _listaToken) return; // respuesta desactualizada, ignorar
|
|
|
|
renderResumen(d.estadisticas_hoy, d.domicilios?.sin_asignar ?? d.sin_asignar_hoy);
|
|
|
|
const tbody = document.getElementById('tabla-body');
|
|
if (!d.data?.length) {
|
|
tbody.innerHTML = '<tr><td colspan="4" class="text-center py-4 text-muted">Sin domicilios</td></tr>';
|
|
document.getElementById('paginacion').innerHTML = '';
|
|
return;
|
|
}
|
|
|
|
tbody.innerHTML = d.data.map(dom => `
|
|
<tr class="dom-row ${domSeleccionado===dom.id?'table-active':''}" id="row-${dom.id}" onclick="verDomicilio(${dom.id})">
|
|
<td>${dom.hora_programada||'—'}</td>
|
|
<td>
|
|
<div class="fw-semibold">${esc(dom.paciente_nombre)}</div>
|
|
<small class="text-muted">${esc(dom.barrio||'')}</small>
|
|
</td>
|
|
<td>${dom.enfermera_nombre ? `<small>${esc(dom.enfermera_nombre)}</small>` : '<span class="text-danger small"><i class="fas fa-exclamation-triangle me-1"></i>Sin asignar</span>'}</td>
|
|
<td>
|
|
<span class="badge bg-${COLOR_DOM[dom.estado]||'secondary'} estado-badge">${esc(dom.estado)}</span>
|
|
${(dom.pago_estado === 'pagado')
|
|
? '<span class="badge rounded-pill ms-1" style="background:#d1fae5;color:#065f46;border:1px solid #34d399">✅ Pagado</span>'
|
|
: (dom.pago_estado === 'exento'
|
|
? '<span class="badge rounded-pill ms-1" style="background:#e0e7ff;color:#3730a3;border:1px solid #818cf8">🔵 Exento</span>'
|
|
: ((parseFloat(dom.valor_domicilio)||0)+(parseFloat(dom.valor_copago)||0) > 0
|
|
? '<span class="badge rounded-pill ms-1" style="background:#fff3cd;color:#664d03;border:1px solid #ffc107">⏳ Cobro</span>'
|
|
: ''))}
|
|
</td>
|
|
</tr>
|
|
`).join('');
|
|
|
|
const el = document.getElementById('paginacion');
|
|
if (d.paginas > 1) {
|
|
el.innerHTML = `
|
|
<small class="text-muted">${d.total} domicilio(s)</small>
|
|
<div class="btn-group btn-group-sm">
|
|
${pag>1?`<button class="btn btn-outline-secondary" onclick="cargarLista(${pag-1})"><i class="fas fa-chevron-left"></i></button>`:''}
|
|
<button class="btn btn-secondary disabled">${pag}/${d.paginas}</button>
|
|
${pag<d.paginas?`<button class="btn btn-outline-secondary" onclick="cargarLista(${pag+1})"><i class="fas fa-chevron-right"></i></button>`:''}
|
|
</div>`;
|
|
} else {
|
|
el.innerHTML = `<small class="text-muted">${d.total} domicilio(s)</small>`;
|
|
}
|
|
}
|
|
|
|
function renderResumen(stats, sinAsignar) {
|
|
if (!stats) return;
|
|
const items = [
|
|
{ label:'Programados', val: stats.programado||0, color:'secondary' },
|
|
{ label:'En camino', val: stats.en_camino||0, color:'primary' },
|
|
{ label:'Completados', val: stats.completado||0, color:'success' },
|
|
{ label:'Sin enfermera',val: sinAsignar||0, color:'danger' },
|
|
];
|
|
document.getElementById('resumen-hoy').innerHTML = items.map(i =>
|
|
`<div class="col-6 col-md-3">
|
|
<div class="card border-0 shadow-sm text-center py-2">
|
|
<div class="fs-4 fw-bold text-${i.color}">${i.val}</div>
|
|
<div class="text-muted small">${i.label}</div>
|
|
</div>
|
|
</div>`
|
|
).join('');
|
|
}
|
|
|
|
// ── Detalle ────────────────────────────────────────────────────────────────
|
|
async function verDomicilio(id) {
|
|
domSeleccionado = id;
|
|
document.querySelectorAll('.dom-row').forEach(r => r.classList.remove('table-active'));
|
|
const rowEl = document.getElementById(`row-${id}`);
|
|
if (rowEl) rowEl.classList.add('table-active');
|
|
|
|
document.getElementById('detail-id').textContent = `#${id}`;
|
|
document.getElementById('detail-body').innerHTML = '<div class="text-center py-3"><i class="fas fa-spinner fa-spin"></i></div>';
|
|
|
|
const r = await fetch(`api/lab/get_domicilios.php?id=${id}&no_stats=1`);
|
|
const d = await r.json();
|
|
const dom = d.domicilio;
|
|
if (!dom) return;
|
|
|
|
// Botones de estado
|
|
const SIGUIENTES = {
|
|
programado: [['confirmado','Confirmar','info'],['cancelado','Cancelar','danger']],
|
|
confirmado: [['en_camino','En camino','primary'],['cancelado','Cancelar','danger']],
|
|
en_camino: [['en_domicilio','En domicilio','warning']],
|
|
en_domicilio:[['completado','Completar','success']],
|
|
};
|
|
const btns = (SIGUIENTES[dom.estado]||[]).map(([e,lbl,col]) =>
|
|
`<button class="btn btn-sm btn-${col}" onclick="cambiarEstado(${dom.id},'${e}')">
|
|
<i class="fas fa-arrow-right me-1"></i>${lbl}
|
|
</button>`
|
|
).join('');
|
|
|
|
document.getElementById('detail-body').innerHTML = `
|
|
<div class="row g-3">
|
|
<div class="col-md-6">
|
|
<dl class="row small">
|
|
<dt class="col-5 text-muted">Paciente</dt><dd class="col-7 fw-semibold">${esc(dom.paciente_nombre)}</dd>
|
|
<dt class="col-5 text-muted">Teléfono</dt><dd class="col-7">${esc(dom.paciente_telefono||'—')}</dd>
|
|
<dt class="col-5 text-muted">Dirección</dt><dd class="col-7">${esc(dom.direccion)}</dd>
|
|
<dt class="col-5 text-muted">Barrio</dt><dd class="col-7">${esc(dom.barrio||'—')}</dd>
|
|
<dt class="col-5 text-muted">Indicaciones</dt><dd class="col-7">${esc(dom.indicaciones_dir||'—')}</dd>
|
|
<dt class="col-5 text-muted">Fecha</dt><dd class="col-7">${esc(dom.fecha_programada)} ${esc(dom.hora_programada||'')}</dd>
|
|
<dt class="col-5 text-muted">Servicio</dt><dd class="col-7">${esc(dom.tipo_servicio||'—')}</dd>
|
|
<dt class="col-5 text-muted">Tipo cliente</dt><dd class="col-7">
|
|
${dom.tipo_cliente === 'seguro'
|
|
? `<span class="badge bg-info text-dark"><i class="fas fa-shield-alt me-1"></i>Seguro${dom.seguro_nombre ? ' — '+esc(dom.seguro_nombre) : ''}</span>`
|
|
: `<span class="badge bg-secondary"><i class="fas fa-wallet me-1"></i>Particular</span>`}
|
|
</dd>
|
|
${dom.autorizacion ? `<dt class="col-5 text-muted">Autorización</dt><dd class="col-7">${esc(dom.autorizacion)}</dd>` : ''}
|
|
${dom.copago_laboratorio ? `<dt class="col-5 text-muted">Copago lab.</dt><dd class="col-7">$${Number(dom.copago_laboratorio).toLocaleString('es-CO')}</dd>` : ''}
|
|
</dl>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<div class="mb-3">
|
|
<label class="form-label small text-muted text-uppercase">Estado actual</label><br>
|
|
<span class="badge bg-${COLOR_DOM[dom.estado]||'secondary'} fs-6">${esc(dom.estado)}</span>
|
|
</div>
|
|
<!-- Enfermera asignada -->
|
|
<div class="mb-3">
|
|
<label class="form-label small text-muted text-uppercase">Enfermera</label>
|
|
${dom.enfermera_nombre
|
|
? `<div class="d-flex align-items-center gap-2">
|
|
<span><i class="fas fa-user-nurse text-success me-1"></i>${esc(dom.enfermera_nombre)}</span>
|
|
<button class="btn btn-xs btn-outline-secondary py-0 px-2" onclick="mostrarAsignacion(${dom.id})">Reasignar</button>
|
|
</div>`
|
|
: `<div class="alert alert-warning py-2 small d-flex align-items-center justify-content-between mb-0">
|
|
<span><i class="fas fa-exclamation-triangle me-2"></i>Sin enfermera asignada</span>
|
|
<button class="btn btn-sm btn-warning" onclick="mostrarAsignacion(${dom.id})"><i class="fas fa-user-plus me-1"></i>Asignar</button>
|
|
</div>`}
|
|
</div>
|
|
<!-- Orden médica -->
|
|
${dom.orden_id ? `
|
|
<div class="mb-3">
|
|
<label class="form-label small text-muted text-uppercase">Orden médica</label>
|
|
<a href="lab_ordenes.php?id=${dom.orden_id}" class="d-block small">
|
|
<i class="fas fa-file-medical me-1"></i>Ver orden #${dom.orden_id}
|
|
</a>
|
|
</div>` : ''}
|
|
<!-- Botones de flujo -->
|
|
<div class="d-flex gap-2 flex-wrap">${btns}</div>
|
|
</div>
|
|
</div>
|
|
<div class="row mt-3">
|
|
<div class="col-12">
|
|
<label class="form-label small text-muted text-uppercase">Exámenes de la orden</label>
|
|
<div class="bg-light p-2 rounded small">${esc(dom.examenes_solicitados||'—')}</div>
|
|
</div>
|
|
</div>
|
|
${(dom.valor_domicilio || dom.valor_copago) ? (() => {
|
|
const totalC = (parseFloat(dom.valor_domicilio)||0)+(parseFloat(dom.valor_copago)||0);
|
|
const PAGO_INFO = {
|
|
pendiente: { bg:'#fff3cd', color:'#664d03', border:'#ffc107', icon:'⏳', txt:'Pago pendiente' },
|
|
pagado: { bg:'#d1fae5', color:'#065f46', border:'#34d399', icon:'✅', txt:'Pagado' },
|
|
exento: { bg:'#e0e7ff', color:'#3730a3', border:'#818cf8', icon:'🔵', txt:'Exento' },
|
|
};
|
|
const pEst = dom.pago_estado || 'pendiente';
|
|
const pInfo = PAGO_INFO[pEst] || PAGO_INFO.pendiente;
|
|
const MODO_LABEL = { efectivo:'💵 Efectivo', transferencia:'📲 Transferencia', otro:'📋 Otro' };
|
|
return `
|
|
<div class="row mt-3">
|
|
<div class="col-12">
|
|
<div class="p-2 rounded" style="background:#d1fae5;border:1px solid #6ee7b7">
|
|
<div class="d-flex justify-content-between align-items-center mb-1">
|
|
<span class="small fw-semibold text-success"><i class="fas fa-cash-register me-1"></i>Cobro al cliente</span>
|
|
<span class="badge rounded-pill small" style="background:${pInfo.bg};color:${pInfo.color};border:1px solid ${pInfo.border}">${pInfo.icon} ${pInfo.txt}</span>
|
|
</div>
|
|
<div class="d-flex gap-3 flex-wrap">
|
|
${dom.valor_domicilio ? `<span class="small">Domicilio: <strong>$${Number(dom.valor_domicilio).toLocaleString('es-CO')}</strong></span>` : ''}
|
|
${dom.valor_copago ? `<span class="small">Copago: <strong>$${Number(dom.valor_copago).toLocaleString('es-CO')}</strong></span>` : ''}
|
|
${totalC > 0 ? `<span class="small fw-bold text-success">Total: $${totalC.toLocaleString('es-CO')}</span>` : ''}
|
|
</div>
|
|
${dom.pago_estado === 'pagado' ? `
|
|
<div class="small mt-1 text-success">
|
|
${MODO_LABEL[dom.pago_modo] || (dom.pago_modo||'')}
|
|
${dom.pago_monto ? ` · $${Number(dom.pago_monto).toLocaleString('es-CO')} cobrado` : ''}
|
|
${dom.pago_notas ? ` · <em>${esc(dom.pago_notas)}</em>` : ''}
|
|
</div>` : ''}
|
|
</div>
|
|
</div>
|
|
</div>`;
|
|
})() : ''}
|
|
${dom.notas_admin ? `
|
|
<div class="row mt-2">
|
|
<div class="col-12">
|
|
<label class="form-label small text-muted text-uppercase">Notas internas</label>
|
|
<div class="bg-light p-2 rounded small">${esc(dom.notas_admin)}</div>
|
|
</div>
|
|
</div>` : ''}
|
|
`;
|
|
}
|
|
|
|
function cerrarDetalle() {
|
|
domSeleccionado = null;
|
|
document.querySelectorAll('.dom-row').forEach(r => r.classList.remove('table-active'));
|
|
document.getElementById('detail-id').textContent = '';
|
|
document.getElementById('detail-body').innerHTML = '<div class="text-center py-5 text-muted"><i class="fas fa-arrow-left me-2"></i>Selecciona un domicilio</div>';
|
|
}
|
|
|
|
// ── Asignación rápida ──────────────────────────────────────────────────────
|
|
async function mostrarAsignacion(domId) {
|
|
const re = await fetch('api/lab/get_enfermeras.php');
|
|
const de = await re.json();
|
|
const opts = (de.data||[]).map(e => `<option value="${e.id}">${esc(e.nombre_completo)} (${e.domicilios_hoy||0} hoy)</option>`).join('');
|
|
|
|
if (!confirm('¿Asignar enfermera?')) return;
|
|
|
|
const sel = prompt(`Selecciona el ID de la enfermera:\n${(de.data||[]).map(e=>`${e.id}: ${e.nombre_completo}`).join('\n')}`);
|
|
if (!sel) return;
|
|
|
|
const rr = await fetch('api/lab/save_asignacion.php', {
|
|
method:'POST',
|
|
headers:{'Content-Type':'application/json'},
|
|
body: JSON.stringify({ domicilio_id: domId, enfermera_id: parseInt(sel) }),
|
|
});
|
|
const dd = await rr.json();
|
|
mostrarToast(dd.success ? dd.message : (dd.error||'Error'), dd.success?'success':'danger');
|
|
if (dd.success) { cargarLista(paginaActual); verDomicilio(domId); }
|
|
}
|
|
|
|
// ── Cambio de estado ───────────────────────────────────────────────────────
|
|
async function cambiarEstado(id, estado) {
|
|
if (!confirm(`¿Cambiar estado a "${estado}"?`)) return;
|
|
const r = await fetch('api/lab/save_domicilio.php', {
|
|
method:'POST',
|
|
headers:{'Content-Type':'application/json'},
|
|
body: JSON.stringify({ id, solo_estado:true, nuevo_estado:estado }),
|
|
});
|
|
const d = await r.json();
|
|
mostrarToast(d.success ? d.message : (d.error||'Error'), d.success?'success':'danger');
|
|
if (d.success) { cargarLista(paginaActual); verDomicilio(id); }
|
|
}
|
|
|
|
// ── Formulario nuevo ───────────────────────────────────────────────────────
|
|
function abrirFormulario() {
|
|
document.getElementById('form-domicilio').reset();
|
|
document.getElementById('dom-id').value = '';
|
|
document.getElementById('dom-fecha').value = document.getElementById('filtro-fecha').value || '<?= date('Y-m-d') ?>';
|
|
modal.show();
|
|
|
|
// Autocompletar paciente desde parámetros URL
|
|
<?php if ($pacIdParam): ?>
|
|
document.getElementById('dom-paciente-id').value = '<?= $pacIdParam ?>';
|
|
document.getElementById('dom-paciente-nombre').value = 'Paciente #<?= $pacIdParam ?>';
|
|
<?php endif; ?>
|
|
}
|
|
|
|
async function guardarDomicilio() {
|
|
const form = document.getElementById('form-domicilio');
|
|
if (!form.checkValidity()) { form.reportValidity(); return; }
|
|
if (!document.getElementById('dom-paciente-id').value) {
|
|
alert('Debes seleccionar un paciente'); return;
|
|
}
|
|
const datos = Object.fromEntries(new FormData(form).entries());
|
|
if (!datos.id) delete datos.id;
|
|
if (!datos.orden_id) delete datos.orden_id;
|
|
|
|
const r = await fetch('api/lab/save_domicilio.php', {
|
|
method:'POST', headers:{'Content-Type':'application/json'},
|
|
body: JSON.stringify(datos),
|
|
});
|
|
const d = await r.json();
|
|
if (d.success) {
|
|
modal.hide();
|
|
mostrarToast(d.message, 'success');
|
|
cargarLista(paginaActual);
|
|
} else {
|
|
mostrarToast(d.error||'Error', 'danger');
|
|
}
|
|
}
|
|
|
|
// Autocompletado de paciente
|
|
let pacTimer;
|
|
document.getElementById('dom-paciente-nombre').addEventListener('input', function() {
|
|
clearTimeout(pacTimer);
|
|
const v = this.value.trim();
|
|
if (v.length < 2) { document.getElementById('pac-suggest').style.display='none'; return; }
|
|
pacTimer = setTimeout(async () => {
|
|
const r = await fetch(`api/lab/get_pacientes.php?busqueda=${encodeURIComponent(v)}&limit=8`);
|
|
const d = await r.json();
|
|
const list = document.getElementById('pac-suggest');
|
|
if (!d.data?.length) { list.style.display='none'; return; }
|
|
list.innerHTML = d.data.map(p =>
|
|
`<button type="button" class="list-group-item list-group-item-action small py-1"
|
|
onclick="selPaciente(${p.id},'${esc(p.nombre_completo)}')">${esc(p.nombre_completo)} — ${esc(p.numero_documento||p.telefono||'')}</button>`
|
|
).join('');
|
|
list.style.display = 'block';
|
|
}, 300);
|
|
});
|
|
|
|
function selPaciente(id, nombre) {
|
|
document.getElementById('dom-paciente-id').value = id;
|
|
document.getElementById('dom-paciente-nombre').value= nombre;
|
|
document.getElementById('pac-suggest').style.display = 'none';
|
|
}
|
|
|
|
function verHoy() {
|
|
document.getElementById('filtro-fecha').value = '<?= date('Y-m-d') ?>';
|
|
cargarLista();
|
|
}
|
|
|
|
// ── Utils ──────────────────────────────────────────────────────────────────
|
|
const esc = s => String(s||'').replace(/[<>&"]/g, c => ({'<':'<','>':'>','&':'&','"':'"'}[c]));
|
|
function mostrarToast(msg, tipo='success') {
|
|
const div = document.createElement('div');
|
|
div.className = `alert alert-${tipo} alert-dismissible position-fixed bottom-0 end-0 m-3`;
|
|
div.style.zIndex = 9999;
|
|
div.innerHTML = `${esc(msg)}<button type="button" class="btn-close" data-bs-dismiss="alert"></button>`;
|
|
document.body.appendChild(div);
|
|
setTimeout(() => div.remove(), 4000);
|
|
}
|
|
</script>
|
|
</body>
|
|
</html>
|