This commit is contained in:
Lizandro Guarnizo
2026-04-21 18:13:53 -05:00
parent 957e264a64
commit 63da89c31a
5 changed files with 309 additions and 135 deletions
+144 -68
View File
@@ -166,15 +166,24 @@ $tab = $_GET['tab'] ?? 'lugares';
<!-- ════════════════════════════════════════
TAB 1 — LUGARES
════════════════════════════════════════ -->
<?php if ($tab === 'lugares'): ?>
<p class="section-title">Estaciones de servicio configurables</p>
<?php if ($tab === 'lugares'):
$lugaresRec = array_values(array_filter($lugares, fn($l) => ($l['tipo'] ?? 'muestras') === 'recepcion'));
$lugaresMues = array_values(array_filter($lugares, fn($l) => ($l['tipo'] ?? 'muestras') === 'muestras'));
?>
<div id="lista-lugares">
<?php foreach ($lugares as $lu): ?>
<!-- ── Escritorios de Recepción ─────────────────── -->
<div class="d-flex align-items-center gap-2 mb-2">
<i class="fas fa-door-open text-primary"></i>
<p class="section-title mb-0">Escritorios de Recepción</p>
<span class="badge bg-primary-subtle text-primary"><?= count($lugaresRec) ?></span>
</div>
<p class="text-muted small mb-3">Cada escritorio es una ventanilla donde un recepcionista atiende pacientes. Aparecerá en la columna izquierda de la pantalla TV global.</p>
<div id="lista-recepcion">
<?php foreach ($lugaresRec as $lu): ?>
<div class="item-row" data-id="<?= $lu['id'] ?>">
<span class="drag-handle text-muted me-1" style="cursor:grab" title="Reordenar">
<i class="fas fa-grip-vertical"></i>
</span>
<span class="drag-handle text-muted me-1" style="cursor:grab"><i class="fas fa-grip-vertical"></i></span>
<i class="fas fa-door-open text-primary me-1" style="font-size:.8rem"></i>
<span class="item-name"><?= htmlspecialchars($lu['nombre']) ?></span>
<?php if ($lu['descripcion']): ?>
<small class="text-muted d-none d-md-inline"><?= htmlspecialchars($lu['descripcion']) ?></small>
@@ -182,10 +191,7 @@ $tab = $_GET['tab'] ?? 'lugares';
<span class="badge <?= $lu['activo'] ? 'bg-success-subtle text-success' : 'bg-secondary-subtle text-secondary' ?> ms-1">
<?= $lu['activo'] ? 'Activo' : 'Inactivo' ?>
</span>
<button class="btn-icon" onclick="copiarUrlDisplay(<?= $lu['id'] ?>)" title="Copiar URL pantalla TV">
<i class="fas fa-tv"></i>
</button>
<button class="btn-icon text-primary" onclick="editarLugar(<?= $lu['id'] ?>,'<?= addslashes($lu['nombre']) ?>','<?= addslashes($lu['descripcion'] ?? '') ?>',<?= $lu['activo'] ?>,<?= $lu['sort_order'] ?>)" title="Editar">
<button class="btn-icon text-primary" onclick="editarLugar(<?= $lu['id'] ?>,'<?= addslashes($lu['nombre']) ?>','<?= addslashes($lu['descripcion'] ?? '') ?>',<?= $lu['activo'] ?>,<?= $lu['sort_order'] ?>,'recepcion')" title="Editar">
<i class="fas fa-pencil-alt"></i>
</button>
<button class="btn-icon text-danger" onclick="eliminarLugar(<?= $lu['id'] ?>, '<?= addslashes($lu['nombre']) ?>')" title="Eliminar">
@@ -193,28 +199,89 @@ $tab = $_GET['tab'] ?? 'lugares';
</button>
</div>
<?php endforeach; ?>
<?php if (empty($lugares)): ?>
<p class="text-muted text-center py-3">No hay lugares configurados</p>
<?php if (empty($lugaresRec)): ?>
<p class="text-muted text-center py-2 small">No hay escritorios de recepción configurados</p>
<?php endif; ?>
</div>
<div class="form-add mt-4">
<p class="section-title mb-3"><i class="fas fa-plus me-1"></i>Agregar lugar</p>
<div class="form-add mt-2 mb-4 p-3" style="background:#eff6ff;border-radius:10px;border:1px solid #bfdbfe">
<p class="section-title mb-3"><i class="fas fa-plus me-1"></i>Agregar escritorio de recepción</p>
<div class="row g-2 align-items-end">
<div class="col-md-4">
<div class="col-md-5">
<label class="form-label small fw-semibold">Nombre <span class="text-danger">*</span></label>
<input type="text" id="lu-nombre" class="form-control form-control-sm" placeholder="Toma de Muestras 3">
<input type="text" id="rec-nombre" class="form-control form-control-sm" placeholder="Recepción 1, Ventanilla 2…">
</div>
<div class="col-md-4">
<label class="form-label small fw-semibold">Descripción</label>
<input type="text" id="lu-desc" class="form-control form-control-sm" placeholder="Opcional">
<input type="text" id="rec-desc" class="form-control form-control-sm" placeholder="Opcional">
</div>
<div class="col-md-2">
<div class="col-md-1">
<label class="form-label small fw-semibold">Orden</label>
<input type="number" id="lu-orden" class="form-control form-control-sm" value="<?= count($lugares) + 1 ?>" min="1">
<input type="number" id="rec-orden" class="form-control form-control-sm" value="<?= count($lugaresRec) + 1 ?>" min="1">
</div>
<div class="col-md-2">
<button class="btn btn-primary btn-sm w-100" onclick="guardarLugar()">
<button class="btn btn-primary btn-sm w-100" onclick="guardarLugar('recepcion')">
<i class="fas fa-save me-1"></i>Guardar
</button>
</div>
</div>
</div>
<hr class="my-3">
<!-- ── Estaciones de Toma de Muestras ──────────── -->
<div class="d-flex align-items-center gap-2 mb-2">
<i class="fas fa-vials text-success"></i>
<p class="section-title mb-0">Estaciones de Toma de Muestras</p>
<span class="badge bg-success-subtle text-success"><?= count($lugaresMues) ?></span>
</div>
<p class="text-muted small mb-3">Cada estación es donde se toman muestras o se realizan pruebas. Aparecerá en la columna derecha de la pantalla TV global.</p>
<div id="lista-muestras">
<?php foreach ($lugaresMues as $lu): ?>
<div class="item-row" data-id="<?= $lu['id'] ?>">
<span class="drag-handle text-muted me-1" style="cursor:grab"><i class="fas fa-grip-vertical"></i></span>
<i class="fas fa-flask text-success me-1" style="font-size:.8rem"></i>
<span class="item-name"><?= htmlspecialchars($lu['nombre']) ?></span>
<?php if ($lu['descripcion']): ?>
<small class="text-muted d-none d-md-inline"><?= htmlspecialchars($lu['descripcion']) ?></small>
<?php endif; ?>
<span class="badge <?= $lu['activo'] ? 'bg-success-subtle text-success' : 'bg-secondary-subtle text-secondary' ?> ms-1">
<?= $lu['activo'] ? 'Activo' : 'Inactivo' ?>
</span>
<button class="btn-icon" onclick="copiarUrlDisplay(<?= $lu['id'] ?>)" title="Copiar URL pantalla TV de este lugar">
<i class="fas fa-tv"></i>
</button>
<button class="btn-icon text-primary" onclick="editarLugar(<?= $lu['id'] ?>,'<?= addslashes($lu['nombre']) ?>','<?= addslashes($lu['descripcion'] ?? '') ?>',<?= $lu['activo'] ?>,<?= $lu['sort_order'] ?>,'muestras')" title="Editar">
<i class="fas fa-pencil-alt"></i>
</button>
<button class="btn-icon text-danger" onclick="eliminarLugar(<?= $lu['id'] ?>, '<?= addslashes($lu['nombre']) ?>')" title="Eliminar">
<i class="fas fa-trash"></i>
</button>
</div>
<?php endforeach; ?>
<?php if (empty($lugaresMues)): ?>
<p class="text-muted text-center py-2 small">No hay estaciones de muestras configuradas</p>
<?php endif; ?>
</div>
<div class="form-add mt-2 mb-4 p-3" style="background:#f0fdf4;border-radius:10px;border:1px solid #bbf7d0">
<p class="section-title mb-3"><i class="fas fa-plus me-1"></i>Agregar estación de toma de muestras</p>
<div class="row g-2 align-items-end">
<div class="col-md-5">
<label class="form-label small fw-semibold">Nombre <span class="text-danger">*</span></label>
<input type="text" id="mue-nombre" class="form-control form-control-sm" placeholder="Toma de Muestras 1, Rayos X…">
</div>
<div class="col-md-4">
<label class="form-label small fw-semibold">Descripción</label>
<input type="text" id="mue-desc" class="form-control form-control-sm" placeholder="Opcional">
</div>
<div class="col-md-1">
<label class="form-label small fw-semibold">Orden</label>
<input type="number" id="mue-orden" class="form-control form-control-sm" value="<?= count($lugaresMues) + 1 ?>" min="1">
</div>
<div class="col-md-2">
<button class="btn btn-success btn-sm w-100" onclick="guardarLugar('muestras')">
<i class="fas fa-save me-1"></i>Guardar
</button>
</div>
@@ -226,11 +293,12 @@ $tab = $_GET['tab'] ?? 'lugares';
<div class="modal-dialog modal-sm">
<div class="modal-content">
<div class="modal-header py-2 px-3">
<h6 class="modal-title">Editar lugar</h6>
<h6 class="modal-title" id="modal-lugar-titulo">Editar lugar</h6>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<input type="hidden" id="edit-lu-id">
<input type="hidden" id="edit-lu-tipo" value="muestras">
<div class="mb-2">
<label class="form-label small fw-semibold">Nombre</label>
<input type="text" id="edit-lu-nombre" class="form-control form-control-sm">
@@ -256,46 +324,45 @@ $tab = $_GET['tab'] ?? 'lugares';
</div>
</div>
<!-- URL panel TV por lugar -->
<div class="mt-4">
<p class="section-title"><i class="fas fa-desktop me-1"></i>URLs de pantalla TV por lugar</p>
<?php foreach ($lugares as $lu): ?>
<!-- URLs de acceso -->
<hr class="my-4">
<p class="section-title"><i class="fas fa-desktop me-1"></i>URLs de pantalla TV</p>
<div class="mb-3 p-3" style="background:#fffbeb;border:1px solid #fde68a;border-radius:10px">
<div class="fw-semibold small mb-2"><i class="fas fa-star me-1 text-warning"></i>Pantalla Global (sala de espera — recomendada)</div>
<p class="text-muted small mb-2">Muestra TODOS los escritorios de recepción y estaciones de muestras en una sola pantalla.</p>
<div class="url-row">
<div class="url-box flex-1" id="url-global"
data-url="<?= htmlspecialchars(BASE_URL . 'erp.php?m=turnero&v=display_global') ?>"
><?= htmlspecialchars(BASE_URL . 'erp.php?m=turnero&v=display_global') ?></div>
<button class="btn btn-outline-secondary btn-sm" onclick="copiarUrl('url-global')"><i class="fas fa-copy"></i></button>
</div>
</div>
<?php if (!empty($lugaresMues)): ?>
<div class="mb-3">
<div class="fw-semibold small text-muted mb-2"><i class="fas fa-flask me-1"></i>Pantallas individuales por estación de muestras</div>
<?php foreach ($lugaresMues as $lu): ?>
<div class="mb-2">
<small class="fw-semibold text-muted d-block mb-1"><?= htmlspecialchars($lu['nombre']) ?></small>
<small class="d-block mb-1 text-muted"><?= htmlspecialchars($lu['nombre']) ?></small>
<div class="url-row">
<div class="url-box flex-1"
id="url-lugar-<?= $lu['id'] ?>"
data-url="<?= htmlspecialchars(BASE_URL . 'modules/turnero/views/display.php?display=lugar&lugar_id=' . $lu['id']) ?>"
><?= htmlspecialchars(BASE_URL . 'modules/turnero/views/display.php?display=lugar&lugar_id=' . $lu['id']) ?></div>
<button class="btn btn-outline-secondary btn-sm" onclick="copiarUrl('url-lugar-<?= $lu['id'] ?>')">
<i class="fas fa-copy"></i>
</button>
<div class="url-box flex-1" id="url-lugar-<?= $lu['id'] ?>"
data-url="<?= htmlspecialchars(BASE_URL . 'erp.php?m=turnero&v=display&display=lugar&lugar_id=' . $lu['id']) ?>"
><?= htmlspecialchars(BASE_URL . 'erp.php?m=turnero&v=display&display=lugar&lugar_id=' . $lu['id']) ?></div>
<button class="btn btn-outline-secondary btn-sm" onclick="copiarUrl('url-lugar-<?= $lu['id'] ?>')"><i class="fas fa-copy"></i></button>
</div>
</div>
<?php endforeach; ?>
<div class="mb-2 mt-3">
<small class="fw-semibold text-muted d-block mb-1">Recepción (pantalla general)</small>
<div class="url-row">
<div class="url-box flex-1"
id="url-recepcion"
data-url="<?= htmlspecialchars(BASE_URL . 'modules/turnero/views/display.php?display=recepcion') ?>"
><?= htmlspecialchars(BASE_URL . 'modules/turnero/views/display.php?display=recepcion') ?></div>
<button class="btn btn-outline-secondary btn-sm" onclick="copiarUrl('url-recepcion')">
<i class="fas fa-copy"></i>
</button>
</div>
</div>
<div class="mb-2 mt-1">
<small class="fw-semibold text-muted d-block mb-1">Kiosko de turnos (tablet/pantalla entrada)</small>
<div class="url-row">
<div class="url-box flex-1"
id="url-kiosko"
data-url="<?= htmlspecialchars(BASE_URL . 'modules/turnero/views/kiosko.php') ?>"
><?= htmlspecialchars(BASE_URL . 'modules/turnero/views/kiosko.php') ?></div>
<button class="btn btn-outline-secondary btn-sm" onclick="copiarUrl('url-kiosko')">
<i class="fas fa-copy"></i>
</button>
</div>
</div>
<?php endif; ?>
<div class="mb-2">
<small class="fw-semibold text-muted d-block mb-1"><i class="fas fa-tablet-alt me-1"></i>Kiosko de turnos (tablet entrada)</small>
<div class="url-row">
<div class="url-box flex-1" id="url-kiosko"
data-url="<?= htmlspecialchars(BASE_URL . 'modules/turnero/views/kiosko.php') ?>"
><?= htmlspecialchars(BASE_URL . 'modules/turnero/views/kiosko.php') ?></div>
<button class="btn btn-outline-secondary btn-sm" onclick="copiarUrl('url-kiosko')"><i class="fas fa-copy"></i></button>
</div>
</div>
@@ -677,15 +744,21 @@ function copiarUrlDisplay(lugarId) {
// ─────────────────────────────────────────────────────────────
// TAB 1: LUGARES
// ─────────────────────────────────────────────────────────────
async function guardarLugar(id = null) {
const nombre = (id ? document.getElementById('edit-lu-nombre') : document.getElementById('lu-nombre'))?.value.trim();
const desc = (id ? document.getElementById('edit-lu-desc') : document.getElementById('lu-desc'))?.value.trim();
const orden = parseInt((id ? document.getElementById('edit-lu-orden') : document.getElementById('lu-orden'))?.value) || 99;
const activo = id ? (document.getElementById('edit-lu-activo')?.checked ? 1 : 0) : 1;
async function guardarLugar(tipoOrId = 'muestras', id = null) {
// tipoOrId puede ser 'recepcion', 'muestras', o un número (id al editar)
const esEdicion = typeof tipoOrId === 'number';
if (esEdicion) { id = tipoOrId; tipoOrId = null; }
const prefijo = esEdicion ? 'edit-lu' : (tipoOrId === 'recepcion' ? 'rec' : 'mue');
const nombre = document.getElementById(esEdicion ? 'edit-lu-nombre' : `${prefijo}-nombre`)?.value.trim();
const desc = document.getElementById(esEdicion ? 'edit-lu-desc' : `${prefijo}-desc`)?.value.trim();
const orden = parseInt(document.getElementById(esEdicion ? 'edit-lu-orden' : `${prefijo}-orden`)?.value) || 99;
const activo = esEdicion ? (document.getElementById('edit-lu-activo')?.checked ? 1 : 0) : 1;
const tipo = esEdicion ? (document.getElementById('edit-lu-tipo')?.value || 'muestras') : tipoOrId;
if (!nombre) { toast('El nombre es requerido', 'error'); return; }
const body = { nombre, descripcion: desc, sort_order: orden, activo };
const body = { nombre, tipo, descripcion: desc, sort_order: orden, activo };
if (id) body.id = id;
try {
@@ -700,12 +773,15 @@ async function guardarLugar(id = null) {
} catch (e) { toast('Error de conexión', 'error'); }
}
function editarLugar(id, nombre, desc, activo, orden) {
document.getElementById('edit-lu-id').value = id;
document.getElementById('edit-lu-nombre').value = nombre;
document.getElementById('edit-lu-desc').value = desc;
document.getElementById('edit-lu-orden').value = orden;
function editarLugar(id, nombre, desc, activo, orden, tipo = 'muestras') {
document.getElementById('edit-lu-id').value = id;
document.getElementById('edit-lu-tipo').value = tipo;
document.getElementById('edit-lu-nombre').value = nombre;
document.getElementById('edit-lu-desc').value = desc;
document.getElementById('edit-lu-orden').value = orden;
document.getElementById('edit-lu-activo').checked = !!activo;
const titulo = tipo === 'recepcion' ? 'Editar escritorio de recepción' : 'Editar estación de muestras';
document.getElementById('modal-lugar-titulo').textContent = titulo;
new bootstrap.Modal(document.getElementById('modalEditarLugar')).show();
}
+47 -39
View File
@@ -258,19 +258,13 @@ $_labColor = preg_match('/^#[0-9a-fA-F]{3,8}$/', $_dispCfg['doc_color'] ?? '')
<!-- Cuerpo: Recepción | Muestras -->
<div class="pg-body" id="pg-body">
<!-- Panel Recepción -->
<!-- Panel Recepción (dinámico: N escritorios) -->
<div class="area-panel" id="panel-recepcion">
<div class="area-header recepcion">
<i class="fas fa-door-open"></i> Recepción
</div>
<div class="area-turno" id="turno-recepcion">
<div class="ring" id="ring-recepcion"></div>
<div class="lbl-destino">Pase a Recepción</div>
<div class="codigo-num" id="cod-recepcion">—</div>
<div class="pac-nombre" id="pac-recepcion"></div>
<div class="prio-chip" id="chip-recepcion">
<i class="fas fa-hourglass-half"></i> En espera
</div>
<div class="lugares-grid" id="rec-grid">
<!-- Se genera en JS según escritorios configurados -->
</div>
</div>
@@ -389,8 +383,8 @@ function anunciarTurno(codigo, destino) {
}
// ── Estado previo ──────────────────────────────────────────────
let lastCodRecepcion = null;
const lastCodLugar = {}; // { lugar_id: codigo }
const lastCodRecepcion = {}; // { desk_id: codigo }
const lastCodLugar = {}; // { lugar_id: codigo }
// ── Escape HTML ────────────────────────────────────────────────
function esc(str) {
@@ -417,36 +411,50 @@ function limpiarColor(panelEl, ringEl, chipEl) {
// ── Render snapshot global ─────────────────────────────────────
function renderSnapshot(snap) {
// ── Recepción ───────────────────────────────────────────
const rec = snap.activo_recepcion;
const panelRec = document.getElementById('turno-recepcion');
const ringRec = document.getElementById('ring-recepcion');
const codRec = document.getElementById('cod-recepcion');
const pacRec = document.getElementById('pac-recepcion');
const chipRec = document.getElementById('chip-recepcion');
// ── Recepción (múltiples escritorios) ───────────────────
const recList = snap.activos_recepcion || [];
const recGrid = document.getElementById('rec-grid');
if (rec) {
const color = rec.prioridad_color || '#1565c0';
if (rec.codigo !== lastCodRecepcion) {
anunciarTurno(rec.codigo, 'Recepción');
ringRec.classList.remove('animar');
void ringRec.offsetWidth;
ringRec.classList.add('animar');
lastCodRecepcion = rec.codigo;
recGrid.innerHTML = recList.map(d => {
const t = d.turno;
const color = t ? (t.prioridad_color || '#1565c0') : null;
const colorStyle = color ? `style="--prio-color:${color};background:${color}12"` : '';
const codStyle = color ? '' : 'style="color:#cbd5e1"';
const chipStyle = color
? `style="background:${color}22;color:${color}"`
: 'style="background:#f1f5f9;color:#94a3b8"';
const chipContent = t
? `<i class='fas fa-ticket-alt'></i> ${esc(t.prioridad_codigo)} — ${esc(t.prioridad_nombre)}`
: `<i class='fas fa-hourglass-half'></i> En espera`;
const codText = t ? esc(t.codigo) : '—';
const pacText = t ? esc(t.paciente_nombre || '') : '';
return `
<div class="lugar-sub" id="sub-rec-${d.desk_id}">
<div class="area-header" style="background:#1e40af;font-size:.7rem;padding:.35rem 1rem">
<i class="fas fa-door-open"></i> ${esc(d.desk_nombre)}
</div>
<div class="area-turno" id="area-rec-${d.desk_id}" ${colorStyle}>
<div class="ring" id="ring-rec-${d.desk_id}"></div>
<div class="lbl-destino">Pase a ${esc(d.desk_nombre)}</div>
<div class="codigo-num" id="cod-rec-${d.desk_id}" ${codStyle}>${codText}</div>
<div class="pac-nombre">${pacText}</div>
<div class="prio-chip" id="chip-rec-${d.desk_id}" ${chipStyle}>${chipContent}</div>
</div>
</div>`;
}).join('');
// Sonido/animación por desk
recList.forEach(d => {
const t = d.turno;
if (!t) { lastCodRecepcion[d.desk_id] = null; return; }
if (t.codigo !== lastCodRecepcion[d.desk_id]) {
anunciarTurno(t.codigo, d.desk_nombre);
const ring = document.getElementById(`ring-rec-${d.desk_id}`);
if (ring) { ring.classList.remove('animar'); void ring.offsetWidth; ring.classList.add('animar'); }
lastCodRecepcion[d.desk_id] = t.codigo;
}
aplicarColor(panelRec, ringRec, chipRec, color);
codRec.textContent = rec.codigo;
codRec.style.color = '';
pacRec.textContent = rec.paciente_nombre || '';
chipRec.innerHTML = `<i class="fas fa-ticket-alt"></i> ${esc(rec.prioridad_codigo)} — ${esc(rec.prioridad_nombre)}`;
} else {
limpiarColor(panelRec, ringRec, chipRec);
codRec.textContent = '—';
codRec.style.color = '#cbd5e1';
pacRec.textContent = '';
chipRec.innerHTML = '<i class="fas fa-hourglass-half"></i> En espera';
lastCodRecepcion = null;
}
});
// ── Lugares (muestras) ──────────────────────────────────
const lugares = snap.lugares || [];