turnero: panel especialidades (ginecología/pediatría) en toma de muestras
- Sección en la parte superior de lugar.php muestra pacientes esperando en Ginecología y Pediatría (inicio_lugar_at IS NOT NULL, fin_lugar_at IS NULL) - Chips de acceso rápido (botones) al tope de la cola para navegar directo - Contadores de espera actualizados cada 30s - Clicking en un paciente o "Ir →" navega a esa vista completa para atenderlo - Solo aparece si existen esos lugares en DB y hay pacientes esperando - Nuevo: get_especialidades.php para consultar pacientes de lugares especiales Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 4.6
parent
b1d1c0896f
commit
e634eb8932
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
/**
|
||||
* GET get_especialidades.php?ids[]=20&ids[]=21
|
||||
* Devuelve pacientes esperando en lugares de especialidad (ginecología, pediatría).
|
||||
* "Esperando" = inicio_lugar_at IS NOT NULL AND fin_lugar_at IS NULL
|
||||
*/
|
||||
require_once __DIR__ . '/_helpers.php';
|
||||
requireTurnero();
|
||||
requireMethod('GET');
|
||||
|
||||
$pdo = db();
|
||||
|
||||
$ids = array_filter(array_map('intval', (array)($_GET['ids'] ?? [])));
|
||||
if (!$ids) jsonOk(['grupos' => []]);
|
||||
|
||||
$sesionId = (int)($pdo->query(
|
||||
"SELECT id FROM turnero_sesiones ORDER BY id DESC LIMIT 1"
|
||||
)->fetchColumn() ?: 0);
|
||||
|
||||
if (!$sesionId) jsonOk(['grupos' => []]);
|
||||
|
||||
$in = implode(',', $ids);
|
||||
$lugares = $pdo->query(
|
||||
"SELECT id, nombre FROM turnero_lugares WHERE id IN ($in) AND activo=1 ORDER BY sort_order"
|
||||
)->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
$grupos = [];
|
||||
foreach ($lugares as $l) {
|
||||
$lid = (int)$l['id'];
|
||||
$stmt = $pdo->prepare("
|
||||
SELECT t.id, t.paciente_nombre, t.inicio_lugar_at, ts.numero_orden
|
||||
FROM turnero_turnos t
|
||||
JOIN turnero_solicitudes ts ON ts.turno_id = t.id
|
||||
WHERE t.sesion_id = ? AND t.lugar_destino_id = ?
|
||||
AND t.inicio_lugar_at IS NOT NULL AND t.fin_lugar_at IS NULL
|
||||
ORDER BY ts.numero_orden ASC
|
||||
");
|
||||
$stmt->execute([$sesionId, $lid]);
|
||||
$grupos[] = [
|
||||
'lugar_id' => $lid,
|
||||
'lugar_nombre' => $l['nombre'],
|
||||
'pacientes' => $stmt->fetchAll(PDO::FETCH_ASSOC),
|
||||
];
|
||||
}
|
||||
|
||||
jsonOk(['grupos' => $grupos]);
|
||||
@@ -82,6 +82,16 @@ try {
|
||||
$stmt->execute([(int)($_SESSION['admin_user']['id'] ?? 0)]);
|
||||
$adminFirmaSvg = $stmt->fetchColumn() ?: null;
|
||||
} catch (\Throwable $_) {}
|
||||
|
||||
// Especialidades (Ginecología, Pediatría) — excluir el lugar actual
|
||||
$especialidades = [];
|
||||
foreach ($lugares as $_el) {
|
||||
$_en = mb_strtolower($_el['nombre']);
|
||||
if ((str_contains($_en, 'ginecol') || str_contains($_en, 'pediatr'))
|
||||
&& (int)$_el['id'] !== $lugarIdParam) {
|
||||
$especialidades[] = $_el;
|
||||
}
|
||||
}
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="es">
|
||||
@@ -148,6 +158,49 @@ try {
|
||||
|
||||
.cola-items { flex: 1; overflow-y: auto; padding: .5rem; }
|
||||
|
||||
/* ── Especialidades en cola ── */
|
||||
.esp-toplinks {
|
||||
display: flex; gap: .35rem; flex-wrap: wrap; flex-shrink: 0;
|
||||
padding: .45rem .75rem; background: #fff;
|
||||
border-bottom: 1px solid #f1f5f9;
|
||||
}
|
||||
.esp-toplink {
|
||||
display: inline-flex; align-items: center; gap: .3rem;
|
||||
padding: .2rem .6rem; border-radius: 99px; font-size: .75rem; font-weight: 600;
|
||||
color: var(--esp-c, #64748b);
|
||||
border: 1.5px solid color-mix(in srgb, var(--esp-c, #64748b) 30%, transparent);
|
||||
background: color-mix(in srgb, var(--esp-c, #64748b) 8%, #fff);
|
||||
text-decoration: none; white-space: nowrap; transition: background .12s;
|
||||
}
|
||||
.esp-toplink:hover { background: color-mix(in srgb, var(--esp-c) 18%, #fff); }
|
||||
.esp-toplink .esp-cnt {
|
||||
background: var(--esp-c); color: #fff; border-radius: 99px;
|
||||
padding: 0 5px; font-size: .65rem; min-width: 16px; text-align: center;
|
||||
}
|
||||
#esp-panel { flex-shrink: 0; border-bottom: 2px solid #e2e8f0; }
|
||||
.esp-grupo { padding: .35rem .6rem; }
|
||||
.esp-grupo + .esp-grupo { border-top: 1px solid #f1f5f9; }
|
||||
.esp-grupo-hdr {
|
||||
display: flex; align-items: center; gap: .35rem;
|
||||
font-size: .72rem; font-weight: 700; color: #475569; margin-bottom: .2rem;
|
||||
}
|
||||
.esp-grupo-count {
|
||||
background: var(--esp-gc); color: #fff; border-radius: 99px; padding: 0 5px; font-size: .65rem;
|
||||
}
|
||||
.esp-grupo-ir {
|
||||
margin-left: auto; font-size: .7rem; font-weight: 600;
|
||||
color: var(--esp-gc); text-decoration: none;
|
||||
}
|
||||
.esp-grupo-ir:hover { text-decoration: underline; }
|
||||
.esp-pac-row {
|
||||
display: flex; align-items: center; gap: .4rem;
|
||||
padding: .18rem .25rem; border-radius: 5px; font-size: .77rem;
|
||||
text-decoration: none; color: #1e293b; transition: background .1s;
|
||||
}
|
||||
.esp-pac-row:hover { background: #f1f5f9; }
|
||||
.esp-pac-orden { font-weight: 700; font-family: monospace; color: var(--esp-gc); font-size: .7rem; flex-shrink: 0; }
|
||||
.esp-pac-hora { margin-left: auto; font-size: .68rem; color: #94a3b8; flex-shrink: 0; }
|
||||
|
||||
.cola-section-sep {
|
||||
font-size: .65rem; text-transform: uppercase; letter-spacing: .06em; font-weight: 700;
|
||||
color: #94a3b8; padding: .3rem .4rem .15rem; margin-top: .25rem;
|
||||
@@ -605,6 +658,24 @@ require_once __DIR__ . '/../../../shared/components/sidebar.php';
|
||||
<i class="fas fa-bell"></i>Llamar siguiente
|
||||
</button>
|
||||
</div>
|
||||
<?php if ($especialidades): ?>
|
||||
<div class="esp-toplinks">
|
||||
<?php foreach ($especialidades as $_esp):
|
||||
$_esGine = mb_stripos($_esp['nombre'], 'ginecol') !== false;
|
||||
$_espC = $_esGine ? '#ec4899' : '#0ea5e9';
|
||||
$_espI = $_esGine ? 'fa-venus' : 'fa-child';
|
||||
?>
|
||||
<a href="<?= BASE_URL ?>erp.php?m=turnero&v=lugar&lugar_id=<?= (int)$_esp['id'] ?>"
|
||||
class="esp-toplink" style="--esp-c:<?= $_espC ?>">
|
||||
<i class="fas <?= $_espI ?>"></i>
|
||||
<?= htmlspecialchars($_esp['nombre']) ?>
|
||||
<span class="esp-cnt" id="esp-cnt-<?= (int)$_esp['id'] ?>">—</span>
|
||||
</a>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
<div id="esp-panel" style="display:none"></div>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="cola-items" id="cola-items">
|
||||
<div class="cola-vacia">
|
||||
<i class="fas fa-hourglass-start fa-2x mb-2 d-block"></i>
|
||||
@@ -928,6 +999,7 @@ const BASE_WA = '<?= BASE_URL ?>';
|
||||
const LUGAR_NOMBRE = document.getElementById('lbl-lugar-titulo')?.textContent || '<?= addslashes($lugarNombre) ?>';
|
||||
const LUGAR_FORM_MODO = '<?= $lugarFormModo ?>';
|
||||
const LUGAR_TIPO = '<?= $lugarTipo ?>';
|
||||
const ESP_LUGARES = <?= json_encode(array_values($especialidades)) ?>;
|
||||
|
||||
// ── Arranque ──────────────────────────────────────────────────
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
@@ -937,7 +1009,9 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
function iniciarPuesto() {
|
||||
recuperarTurnoActivo();
|
||||
cargarCola();
|
||||
_cargarEspecialidades();
|
||||
pollingColaId = setInterval(cargarCola, 7000);
|
||||
setInterval(_cargarEspecialidades, 30000);
|
||||
|
||||
document.addEventListener('visibilitychange', () => {
|
||||
if (document.hidden) {
|
||||
@@ -946,6 +1020,7 @@ function iniciarPuesto() {
|
||||
pollingColaId = pollingConsentId = null;
|
||||
} else if (lugarId) {
|
||||
cargarCola();
|
||||
_cargarEspecialidades();
|
||||
pollingColaId = setInterval(cargarCola, 7000);
|
||||
}
|
||||
});
|
||||
@@ -2321,6 +2396,47 @@ function mostrarToast(msg, type = 'info', duration = 2500) {
|
||||
function mostrarError(msg) { mostrarToast(msg, 'error', 4000); }
|
||||
function mostrarLlamando(cod) { mostrarToast('Llamando turno ' + cod + '…', 'info', 2000); }
|
||||
|
||||
async function _cargarEspecialidades() {
|
||||
if (!ESP_LUGARES.length) return;
|
||||
try {
|
||||
const qs = ESP_LUGARES.map(l => `ids[]=${l.id}`).join('&');
|
||||
const res = await fetch(`${API}get_especialidades.php?${qs}`);
|
||||
const j = await res.json();
|
||||
if (j.ok) _renderEspecialidades(j.grupos);
|
||||
} catch {}
|
||||
}
|
||||
|
||||
function _renderEspecialidades(grupos) {
|
||||
const panel = document.getElementById('esp-panel');
|
||||
if (!panel) return;
|
||||
let html = '';
|
||||
for (const g of grupos) {
|
||||
const esGine = g.lugar_nombre.toLowerCase().includes('ginecol');
|
||||
const color = esGine ? '#ec4899' : '#0ea5e9';
|
||||
const icon = esGine ? 'fa-venus' : 'fa-child';
|
||||
const url = `${BASE_WA}erp.php?m=turnero&v=lugar&lugar_id=${g.lugar_id}`;
|
||||
const cnt = document.getElementById(`esp-cnt-${g.lugar_id}`);
|
||||
if (cnt) cnt.textContent = g.pacientes.length;
|
||||
if (!g.pacientes.length) continue;
|
||||
html += `<div class="esp-grupo" style="--esp-gc:${color}">
|
||||
<div class="esp-grupo-hdr">
|
||||
<i class="fas ${icon}" style="color:${color}"></i>
|
||||
${escHtml(g.lugar_nombre)}
|
||||
<span class="esp-grupo-count">${g.pacientes.length}</span>
|
||||
<a href="${url}" class="esp-grupo-ir">Ir <i class="fas fa-arrow-right fa-xs"></i></a>
|
||||
</div>
|
||||
${g.pacientes.map(p => `
|
||||
<a href="${url}" class="esp-pac-row">
|
||||
<span class="esp-pac-orden">#${p.numero_orden}</span>
|
||||
<span>${escHtml(p.paciente_nombre)}</span>
|
||||
<span class="esp-pac-hora">${formatHora(p.inicio_lugar_at)}</span>
|
||||
</a>`).join('')}
|
||||
</div>`;
|
||||
}
|
||||
panel.innerHTML = html;
|
||||
panel.style.display = html ? '' : 'none';
|
||||
}
|
||||
|
||||
/* ── Ocupación exclusiva del puesto ──────────────────────────── */
|
||||
(function(_ID, _NOMBRE, _VIEW, _PARAM) {
|
||||
if (!_ID) return;
|
||||
|
||||
Reference in New Issue
Block a user