Files
whatsapp/DOCUMENTACION_TURNERO.html
2026-06-09 16:44:39 -05:00

1023 lines
67 KiB
HTML

<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Flujo Completo del Turnero — Laboratorio Clínico</title>
<style>
*{box-sizing:border-box;margin:0;padding:0}
body{font-family:'Segoe UI',system-ui,-apple-system,sans-serif;background:#0a0f1a;color:#e2e8f0;line-height:1.6}
/* ── HERO ── */
.hero{background:linear-gradient(135deg,#0f172a 0%,#1e3a5f 50%,#0f766e 100%);padding:3rem 2rem;text-align:center;position:relative;overflow:hidden}
.hero::before{content:'';position:absolute;inset:0;background:url("data:image/svg+xml,%3Csvg width='60' height='60' viewBox='0 0 60 60' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='none' fill-rule='evenodd'%3E%3Cg fill='%23ffffff' fill-opacity='0.03'%3E%3Cpath d='M36 34v-4h-2v4h-4v2h4v4h2v-4h4v-2h-4zm0-30V0h-2v4h-4v2h4v4h2V6h4V4h-4zM6 34v-4H4v4H0v2h4v4h2v-4h4v-2H6zM6 4V0H4v4H0v2h4v4h2V6h4V4H6z'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E")}
.hero h1{font-size:2.4rem;font-weight:900;letter-spacing:-.03em;position:relative}
.hero h1 span{background:linear-gradient(90deg,#60a5fa,#34d399);-webkit-background-clip:text;-webkit-text-fill-color:transparent}
.hero .sub{font-size:1.05rem;color:rgba(255,255,255,.7);margin-top:.5rem;position:relative}
.hero .badges{display:flex;gap:.8rem;justify-content:center;margin-top:1.5rem;flex-wrap:wrap;position:relative}
.hero .badge{background:rgba(255,255,255,.1);border:1px solid rgba(255,255,255,.15);border-radius:99px;padding:.35rem 1.2rem;font-size:.82rem;font-weight:600;backdrop-filter:blur(8px)}
/* ── NAV ── */
.nav-sticky{position:sticky;top:0;z-index:100;background:rgba(15,23,42,.92);backdrop-filter:blur(16px);border-bottom:1px solid rgba(255,255,255,.08);padding:.6rem 2rem;display:flex;gap:.5rem;overflow-x:auto;scrollbar-width:none}
.nav-sticky::-webkit-scrollbar{display:none}
.nav-sticky a{color:#94a3b8;text-decoration:none;font-size:.78rem;font-weight:600;padding:.35rem .9rem;border-radius:99px;white-space:nowrap;transition:all .2s;border:1px solid transparent}
.nav-sticky a:hover,.nav-sticky a.active{color:#fff;background:rgba(96,165,250,.15);border-color:rgba(96,165,250,.3)}
/* ── CONTAINER ── */
.container{max-width:1200px;margin:0 auto;padding:2rem 1.5rem}
/* ── SECTION ── */
.section{margin-bottom:3rem}
.section-title{display:flex;align-items:center;gap:.8rem;margin-bottom:1.5rem}
.section-title .num{width:42px;height:42px;border-radius:12px;display:flex;align-items:center;justify-content:center;font-size:1.1rem;font-weight:900;color:#fff;flex-shrink:0}
.section-title h2{font-size:1.35rem;font-weight:800;letter-spacing:-.02em}
.section-title .tag{font-size:.68rem;font-weight:700;padding:.2rem .7rem;border-radius:99px;text-transform:uppercase;letter-spacing:.05em}
/* ── FLOW DIAGRAM ── */
.flow{display:flex;flex-direction:column;gap:0;position:relative}
.flow-step{display:grid;grid-template-columns:80px 1fr;gap:0;position:relative}
.flow-line{display:flex;flex-direction:column;align-items:center;position:relative}
.flow-dot{width:48px;height:48px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:1.2rem;color:#fff;font-weight:800;z-index:2;flex-shrink:0;box-shadow:0 0 20px rgba(0,0,0,.3)}
.flow-connector{width:3px;flex:1;min-height:20px;position:relative}
.flow-connector::after{content:'';position:absolute;left:50%;top:0;bottom:0;width:3px;transform:translateX(-50%);background:linear-gradient(180deg,var(--line-from,#3b82f6),var(--line-to,#3b82f6));border-radius:2px}
.flow-content{padding:.8rem 0 1.8rem 0}
.flow-card{background:rgba(255,255,255,.04);border:1px solid rgba(255,255,255,.08);border-radius:16px;padding:1.3rem 1.5rem;transition:all .3s}
.flow-card:hover{background:rgba(255,255,255,.07);border-color:rgba(255,255,255,.15);transform:translateX(4px)}
.flow-card h3{font-size:1.05rem;font-weight:700;margin-bottom:.5rem;display:flex;align-items:center;gap:.5rem}
.flow-card p{font-size:.88rem;color:#94a3b8;line-height:1.65}
.flow-card .detail-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(200px,1fr));gap:.6rem;margin-top:.8rem}
.flow-card .detail-item{background:rgba(255,255,255,.04);border:1px solid rgba(255,255,255,.06);border-radius:10px;padding:.6rem .8rem}
.flow-card .detail-item .dl{font-size:.65rem;text-transform:uppercase;letter-spacing:.06em;color:#64748b;margin-bottom:.15rem}
.flow-card .detail-item .dv{font-size:.82rem;font-weight:600;color:#e2e8f0}
.flow-card .code-ref{display:inline-block;background:rgba(96,165,250,.12);color:#60a5fa;font-family:'SF Mono',Consolas,monospace;font-size:.72rem;padding:.15rem .55rem;border-radius:6px;margin-top:.5rem}
.flow-card .api-badge{display:inline-flex;align-items:center;gap:.3rem;background:rgba(52,211,153,.1);color:#34d399;font-family:'SF Mono',Consolas,monospace;font-size:.7rem;padding:.2rem .6rem;border-radius:6px;margin-top:.3rem;margin-right:.3rem}
.flow-card .view-badge{display:inline-flex;align-items:center;gap:.3rem;background:rgba(168,85,247,.1);color:#a855f7;font-family:'SF Mono',Consolas,monospace;font-size:.7rem;padding:.2rem .6rem;border-radius:6px;margin-top:.3rem;margin-right:.3rem}
/* ── STATE MACHINE ── */
.state-machine{background:rgba(255,255,255,.03);border:1px solid rgba(255,255,255,.08);border-radius:20px;padding:2rem;margin:2rem 0}
.state-machine h3{font-size:1.1rem;font-weight:800;margin-bottom:1.2rem;text-align:center;color:#60a5fa}
.states-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(140px,1fr));gap:.8rem;margin-bottom:1.5rem}
.state-chip{text-align:center;padding:.8rem .5rem;border-radius:12px;font-size:.78rem;font-weight:700;border:2px solid}
.state-chip .s-icon{font-size:1.4rem;display:block;margin-bottom:.3rem}
.transitions{display:grid;grid-template-columns:repeat(auto-fill,minmax(280px,1fr));gap:.5rem}
.transition-item{display:flex;align-items:center;gap:.5rem;font-size:.78rem;padding:.4rem .7rem;background:rgba(255,255,255,.03);border-radius:8px}
.transition-item .arrow{color:#60a5fa;font-weight:800}
.transition-item .cond{color:#fbbf24;font-size:.68rem;font-style:italic}
/* ── PRIORITY TABLE ── */
.prio-table{width:100%;border-collapse:separate;border-spacing:0;background:rgba(255,255,255,.03);border:1px solid rgba(255,255,255,.08);border-radius:16px;overflow:hidden;margin:1rem 0}
.prio-table th{background:rgba(255,255,255,.06);padding:.7rem 1rem;font-size:.7rem;text-transform:uppercase;letter-spacing:.07em;color:#94a3b8;text-align:left;font-weight:700}
.prio-table td{padding:.65rem 1rem;font-size:.85rem;border-top:1px solid rgba(255,255,255,.05)}
.prio-table tr:hover td{background:rgba(255,255,255,.03)}
.prio-dot{width:14px;height:14px;border-radius:50%;display:inline-block;vertical-align:middle;margin-right:.5rem}
/* ── ACTORS ── */
.actors-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(240px,1fr));gap:1rem;margin:1.5rem 0}
.actor-card{background:rgba(255,255,255,.04);border:1px solid rgba(255,255,255,.08);border-radius:16px;padding:1.3rem;text-align:center;transition:all .3s}
.actor-card:hover{transform:translateY(-4px);border-color:rgba(255,255,255,.2)}
.actor-card .a-icon{font-size:2.2rem;margin-bottom:.5rem}
.actor-card h4{font-size:.95rem;font-weight:700;margin-bottom:.3rem}
.actor-card p{font-size:.78rem;color:#94a3b8}
.actor-card .a-views{margin-top:.6rem;display:flex;gap:.3rem;justify-content:center;flex-wrap:wrap}
.actor-card .a-views span{font-size:.65rem;background:rgba(255,255,255,.08);padding:.15rem .5rem;border-radius:6px;color:#cbd5e1}
/* ── SCREENSHOTS MOCKUP ── */
.screen-mock{background:rgba(255,255,255,.03);border:1px solid rgba(255,255,255,.1);border-radius:16px;overflow:hidden;margin:1rem 0}
.screen-mock .mock-bar{background:rgba(255,255,255,.06);padding:.4rem .8rem;display:flex;align-items:center;gap:.5rem}
.screen-mock .mock-bar .dots{display:flex;gap:.3rem}
.screen-mock .mock-bar .dots span{width:8px;height:8px;border-radius:50%}
.screen-mock .mock-bar .url{flex:1;background:rgba(255,255,255,.06);border-radius:6px;padding:.2rem .6rem;font-size:.7rem;color:#94a3b8;font-family:monospace}
.screen-mock .mock-body{padding:1.5rem;min-height:120px}
/* ── INFO BOXES ── */
.info-box{border-radius:14px;padding:1.2rem 1.4rem;margin:1rem 0;border-left:4px solid}
.info-box.blue{background:rgba(59,130,246,.08);border-color:#3b82f6}
.info-box.green{background:rgba(34,197,94,.08);border-color:#22c55e}
.info-box.yellow{background:rgba(234,179,8,.08);border-color:#eab308}
.info-box.purple{background:rgba(168,85,247,.08);border-color:#a855f7}
.info-box h4{font-size:.88rem;font-weight:700;margin-bottom:.4rem;display:flex;align-items:center;gap:.4rem}
.info-box p{font-size:.82rem;color:#94a3b8;line-height:1.6}
/* ── SEQUENCE DIAGRAM ── */
.seq-diagram{overflow-x:auto;margin:1.5rem 0}
.seq-table{width:100%;border-collapse:collapse;min-width:700px}
.seq-table th{padding:.5rem;font-size:.7rem;text-transform:uppercase;letter-spacing:.05em;color:#94a3b8;background:rgba(255,255,255,.04);border-bottom:2px solid rgba(255,255,255,.08)}
.seq-table td{padding:.5rem;text-align:center;border-bottom:1px solid rgba(255,255,255,.04);font-size:.78rem;position:relative}
.seq-table td .seq-arrow{display:inline-block;padding:.2rem .6rem;border-radius:6px;font-size:.7rem;font-weight:600;white-space:nowrap}
.seq-table td .seq-arrow.req{background:rgba(96,165,250,.15);color:#60a5fa}
.seq-table td .seq-arrow.res{background:rgba(52,211,153,.15);color:#34d399}
.seq-table td .seq-arrow.sse{background:rgba(251,191,36,.15);color:#fbbf24}
.seq-table td .seq-arrow.wa{background:rgba(34,197,94,.15);color:#22c55e}
/* ── FOOTER ── */
.footer{text-align:center;padding:2rem;font-size:.75rem;color:#475569;border-top:1px solid rgba(255,255,255,.05)}
/* ── RESPONSIVE ── */
@media(max-width:768px){
.hero h1{font-size:1.6rem}
.flow-step{grid-template-columns:50px 1fr}
.flow-dot{width:36px;height:36px;font-size:.9rem}
.container{padding:1rem}
.states-grid{grid-template-columns:repeat(auto-fill,minmax(100px,1fr))}
.actors-grid{grid-template-columns:1fr}
}
/* ── PRINT ── */
@media print{
body{background:#fff;color:#1e293b}
.nav-sticky{display:none}
.hero{background:#1e3a5f!important;-webkit-print-color-adjust:exact;print-color-adjust:exact}
.flow-card,.actor-card,.state-chip,.info-box,.screen-mock{break-inside:avoid}
}
</style>
</head>
<body>
<!-- ════════════════════════════════════════════════════════════ -->
<!-- HERO -->
<!-- ════════════════════════════════════════════════════════════ -->
<div class="hero">
<h1>🏥 Sistema <span>Turnero Inteligente</span></h1>
<div class="sub">Flujo completo del proceso — Desde la llegada del paciente hasta la toma de muestras</div>
<div class="badges">
<span class="badge">📋 9 Tablas en BD</span>
<span class="badge">⚡ 22 APIs REST</span>
<span class="badge">🖥️ 9 Vistas</span>
<span class="badge">📡 SSE en Tiempo Real</span>
<span class="badge">📱 WhatsApp Integration</span>
</div>
</div>
<!-- ════════════════════════════════════════════════════════════ -->
<!-- NAV -->
<!-- ════════════════════════════════════════════════════════════ -->
<div class="nav-sticky">
<a href="#actores">Actores</a>
<a href="#prioridades">Prioridades</a>
<a href="#estados">Estados</a>
<a href="#flujo">Flujo Completo</a>
<a href="#kiosko">1. Kiosko</a>
<a href="#recepcion">2. Recepción</a>
<a href="#consentimientos">3. Consentimientos</a>
<a href="#muestras">4. Toma de Muestras</a>
<a href="#pantallas">5. Pantallas TV</a>
<a href="#admin">6. Admin</a>
<a href="#secuencia">Diagrama de Secuencia</a>
</div>
<div class="container">
<!-- ════════════════════════════════════════════════════════════ -->
<!-- ACTORES -->
<!-- ════════════════════════════════════════════════════════════ -->
<div class="section" id="actores">
<div class="section-title">
<div class="num" style="background:linear-gradient(135deg,#6366f1,#8b5cf6)">👥</div>
<div>
<h2>Actores del Sistema</h2>
<span class="tag" style="background:rgba(99,102,241,.15);color:#818cf8">Quiénes intervienen</span>
</div>
</div>
<div class="actors-grid">
<div class="actor-card">
<div class="a-icon">🧑‍🦽</div>
<h4>Paciente</h4>
<p>Llega al laboratorio, usa el kiosko táctil para obtener su turno, firma consentimientos desde su celular vía WhatsApp.</p>
<div class="a-views">
<span>kiosko.php</span>
<span>ver_formulario_enviado.php</span>
</div>
</div>
<div class="actor-card">
<div class="a-icon">👩‍💼</div>
<h4>Recepcionista</h4>
<p>Llama turnos, vincula paciente, selecciona exámenes, registra pago, envía consentimientos y pasa al paciente al área de muestras.</p>
<div class="a-views">
<span>recepcion.php</span>
<span>display_recepcion.php</span>
</div>
</div>
<div class="actor-card">
<div class="a-icon">🧑‍⚕️</div>
<h4>Auxiliar de Muestras</h4>
<p>Llama turnos de su estación, verifica consentimientos firmados, inicia atención, toma la muestra y finaliza el turno.</p>
<div class="a-views">
<span>lugar.php</span>
<span>display (por lugar)</span>
</div>
</div>
<div class="actor-card">
<div class="a-icon">⚙️</div>
<h4>Administrador</h4>
<p>Configura lugares, exámenes, prioridades, plantillas WhatsApp. Monitorea dashboard e historial. Exporta reportes CSV.</p>
<div class="a-views">
<span>configuracion.php</span>
<span>dashboard.php</span>
<span>historial.php</span>
</div>
</div>
<div class="actor-card">
<div class="a-icon">📺</div>
<h4>Pantallas TV</h4>
<p>Muestran en tiempo real el turno en atención y la cola de espera. Se actualizan vía SSE (Server-Sent Events).</p>
<div class="a-views">
<span>display_global.php</span>
<span>display.php</span>
</div>
</div>
</div>
</div>
<!-- ════════════════════════════════════════════════════════════ -->
<!-- PRIORIDADES -->
<!-- ════════════════════════════════════════════════════════════ -->
<div class="section" id="prioridades">
<div class="section-title">
<div class="num" style="background:linear-gradient(135deg,#f59e0b,#ef4444)">🎫</div>
<div>
<h2>Sistema de Prioridades</h2>
<span class="tag" style="background:rgba(245,158,11,.15);color:#fbbf24">Motor de cola</span>
</div>
</div>
<p style="color:#94a3b8;margin-bottom:1rem;font-size:.9rem">
El motor de cola ordena los turnos por <strong style="color:#e2e8f0">peso de prioridad</strong> (menor = más urgente) y luego por <strong style="color:#e2e8f0">orden de llegada</strong> (FIFO). Las prioridades son configurables desde el panel de administración.
</p>
<table class="prio-table">
<thead>
<tr>
<th>Código</th>
<th>Prioridad</th>
<th>Color</th>
<th>Icono</th>
<th>Descripción</th>
<th>Peso</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong style="color:#ef4444">A</strong></td>
<td><span class="prio-dot" style="background:#ef4444"></span>Niños</td>
<td><code style="color:#ef4444">#ef4444</code></td>
<td>👶 fa-child</td>
<td>Pacientes menores de edad</td>
<td>1 (mayor prioridad)</td>
</tr>
<tr>
<td><strong style="color:#f97316">B</strong></td>
<td><span class="prio-dot" style="background:#f97316"></span>Embarazadas</td>
<td><code style="color:#f97316">#f97316</code></td>
<td>💗 fa-heart</td>
<td>Mujeres en estado de embarazo</td>
<td>2</td>
</tr>
<tr>
<td><strong style="color:#eab308">C</strong></td>
<td><span class="prio-dot" style="background:#eab308"></span>Adulto mayor</td>
<td><code style="color:#eab308">#eab308</code></td>
<td>🧓 fa-person-cane</td>
<td>Mayores de 60 años</td>
<td>3</td>
</tr>
<tr>
<td><strong style="color:#8b5cf6">D</strong></td>
<td><span class="prio-dot" style="background:#8b5cf6"></span>Discapacidad</td>
<td><code style="color:#8b5cf6">#8b5cf6</code></td>
<td>♿ fa-wheelchair</td>
<td>Personas con discapacidad</td>
<td>4</td>
</tr>
<tr>
<td><strong style="color:#3b82f6">E</strong></td>
<td><span class="prio-dot" style="background:#3b82f6"></span>Paciente general</td>
<td><code style="color:#3b82f6">#3b82f6</code></td>
<td>🧑 fa-user</td>
<td>Atención general</td>
<td>5</td>
</tr>
<tr>
<td><strong style="color:#6b7280">F</strong></td>
<td><span class="prio-dot" style="background:#6b7280"></span>Muestra pendiente</td>
<td><code style="color:#6b7280">#6b7280</code></td>
<td>🧪 fa-vial</td>
<td>Entrega de muestra tomada previamente</td>
<td>6 (menor prioridad)</td>
</tr>
</tbody>
</table>
<div class="info-box blue">
<h4>💡 Ejemplo de correlativos independientes y ordenamiento</h4>
<p>Cada prioridad tiene su <strong>propia secuencia</strong>:<br>
<strong>A-001</strong> (niño, llegó 8:00) → <strong>E-001</strong> (general, llegó 8:05) → <strong>A-002</strong> (niño, llegó 8:10) → <strong>E-002</strong> (general, llegó 8:15)<br><br>
El motor de cola ordena por <strong>peso de prioridad</strong> y luego por <strong>número correlativo</strong>: primero llama a <strong>A-001</strong> (prioridad A, peso 1), luego <strong>A-002</strong>, después <strong>E-001</strong>, y finalmente <strong>E-002</strong>.</p>
</div>
</div>
<!-- ════════════════════════════════════════════════════════════ -->
<!-- MÁQUINA DE ESTADOS -->
<!-- ════════════════════════════════════════════════════════════ -->
<div class="section" id="estados">
<div class="section-title">
<div class="num" style="background:linear-gradient(135deg,#0ea5e9,#06b6d4)">🔄</div>
<div>
<h2>Máquina de Estados del Turno</h2>
<span class="tag" style="background:rgba(14,165,233,.15);color:#38bdf8">Lifecycle</span>
</div>
</div>
<div class="state-machine">
<h3>Estados posibles de un turno</h3>
<div class="states-grid">
<div class="state-chip" style="background:rgba(254,249,195,.1);border-color:#fde047;color:#fde047">
<span class="s-icon"></span>espera
</div>
<div class="state-chip" style="background:rgba(219,234,254,.1);border-color:#93c5fd;color:#93c5fd">
<span class="s-icon">🛎️</span>en_recepcion
</div>
<div class="state-chip" style="background:rgba(253,230,138,.1);border-color:#fcd34d;color:#fcd34d">
<span class="s-icon">🚶</span>en_espera_lugar
</div>
<div class="state-chip" style="background:rgba(220,252,231,.1);border-color:#86efac;color:#86efac">
<span class="s-icon">🔬</span>en_servicio
</div>
<div class="state-chip" style="background:rgba(241,245,249,.1);border-color:#cbd5e1;color:#cbd5e1">
<span class="s-icon"></span>finalizado
</div>
<div class="state-chip" style="background:rgba(254,226,226,.1);border-color:#fca5a5;color:#fca5a5">
<span class="s-icon">🚫</span>ausente
</div>
<div class="state-chip" style="background:rgba(241,245,249,.08);border-color:#9ca3af;color:#9ca3af">
<span class="s-icon"></span>cancelado
</div>
</div>
<h3 style="margin-top:1.5rem">Transiciones permitidas</h3>
<div class="transitions">
<div class="transition-item">
<span style="color:#fde047">espera</span>
<span class="arrow"></span>
<span style="color:#93c5fd">en_recepcion</span>
<span class="cond">(recepcionista llama)</span>
</div>
<div class="transition-item">
<span style="color:#93c5fd">en_recepcion</span>
<span class="arrow"></span>
<span style="color:#fcd34d">en_espera_lugar</span>
<span class="cond">(pasa a estación)</span>
</div>
<div class="transition-item">
<span style="color:#fcd34d">en_espera_lugar</span>
<span class="arrow"></span>
<span style="color:#86efac">en_servicio</span>
<span class="cond">(⚠️ requiere consentimientos OK)</span>
</div>
<div class="transition-item">
<span style="color:#86efac">en_servicio</span>
<span class="arrow"></span>
<span style="color:#cbd5e1">finalizado</span>
<span class="cond">(muestra tomada)</span>
</div>
<div class="transition-item">
<span style="color:#86efac">en_servicio</span>
<span class="arrow"></span>
<span style="color:#fcd34d">en_espera_lugar</span>
<span class="cond">(regresar a cola)</span>
</div>
<div class="transition-item">
<span style="color:#94a3b8">cualquier estado</span>
<span class="arrow"></span>
<span style="color:#fca5a5">ausente / cancelado</span>
</div>
</div>
</div>
<div class="info-box yellow">
<h4>🔒 Bloqueo de consentimientos</h4>
<p>La transición <strong>en_espera_lugar → en_servicio</strong> está <strong>bloqueada</strong> si el turno tiene consentimientos en estado <code>pendiente</code>, <code>enviado</code> o <code>visto</code>. Todos deben estar en <code>firmado</code> o <code>rechazado</code> antes de poder iniciar la atención. Esto se valida en <code style="color:#fbbf24">cambiar_estado.php</code>.</p>
</div>
</div>
<!-- ════════════════════════════════════════════════════════════ -->
<!-- FLUJO COMPLETO -->
<!-- ════════════════════════════════════════════════════════════ -->
<div class="section" id="flujo">
<div class="section-title">
<div class="num" style="background:linear-gradient(135deg,#22c55e,#16a34a)">📋</div>
<div>
<h2>Flujo Completo — Paso a Paso</h2>
<span class="tag" style="background:rgba(34,197,94,.15);color:#4ade80">Proceso end-to-end</span>
</div>
</div>
<div class="flow">
<!-- ── PASO 1: KIOSKO ── -->
<div class="flow-step" id="kiosko">
<div class="flow-line">
<div class="flow-dot" style="background:linear-gradient(135deg,#3b82f6,#2563eb)">1</div>
<div class="flow-connector" style="--line-from:#3b82f6;--line-to:#8b5cf6"></div>
</div>
<div class="flow-content">
<div class="flow-card">
<h3>🖥️ Kiosko — El paciente obtiene su turno</h3>
<p>El paciente llega al laboratorio y se acerca al <strong>kiosko táctil</strong> (tablet o pantalla touch). No requiere login — es una pantalla pública siempre activa.</p>
<div class="detail-grid">
<div class="detail-item">
<div class="dl">Paso 1</div>
<div class="dv">Selecciona su tipo de atención (A-F) tocando el botón correspondiente</div>
</div>
<div class="detail-item">
<div class="dl">Paso 2</div>
<div class="dv">Ingresa su número de cédula y opcionalmente su WhatsApp</div>
</div>
<div class="detail-item">
<div class="dl">Paso 3</div>
<div class="dv">El sistema genera un código con correlativo independiente por prioridad (ej: "A-001", "A-002", "B-001", "E-001")</div>
</div>
<div class="detail-item">
<div class="dl">Resultado</div>
<div class="dv">Turno en estado <span style="color:#fde047;font-weight:700">espera</span> — El paciente se sienta a esperar</div>
</div>
</div>
<div style="margin-top:.8rem">
<span class="view-badge">📄 kiosko.php</span>
<span class="api-badge">⚡ create_turno.php</span>
</div>
<div class="info-box blue" style="margin-top:1rem">
<h4>🔐 Seguridad del kiosko</h4>
<p>El kiosko <strong>nunca expone datos de otros pacientes</strong>. Solo muestra el código del turno recién creado y su posición en cola. El campo <code>paciente_nombre</code> almacena la cédula digitada (no el nombre real). El nombre real se vincula después en recepción.</p>
</div>
</div>
</div>
</div>
<!-- ── PASO 2: RECEPCIÓN ── -->
<div class="flow-step" id="recepcion">
<div class="flow-line">
<div class="flow-dot" style="background:linear-gradient(135deg,#8b5cf6,#7c3aed)">2</div>
<div class="flow-connector" style="--line-from:#8b5cf6;--line-to:#ec4899"></div>
</div>
<div class="flow-content">
<div class="flow-card">
<h3>🛎️ Recepción — Vinculación y solicitud</h3>
<p>El recepcionista ve la cola de turnos en espera en su pantalla. Cuando decide atender un turno, lo llama y se abre la <strong>ficha de atención</strong>.</p>
<div class="detail-grid">
<div class="detail-item">
<div class="dl">Llamar turno</div>
<div class="dv">Clic en "Llamar siguiente" o en la campana de un turno específico de la cola</div>
</div>
<div class="detail-item">
<div class="dl">Estado cambia a</div>
<div class="dv"><span style="color:#93c5fd;font-weight:700">en_recepcion</span> — El turno aparece en la pantalla TV de recepción</div>
</div>
<div class="detail-item">
<div class="dl">Buscar paciente</div>
<div class="dv">Busca por nombre o documento en la base de datos de pacientes (reutiliza lab_pacientes)</div>
</div>
<div class="detail-item">
<div class="dl">Seleccionar exámenes</div>
<div class="dv">Marca los exámenes solicitados de un catálogo agrupado por categoría</div>
</div>
<div class="detail-item">
<div class="dl">Lugar destino</div>
<div class="dv">Selecciona a qué estación de toma de muestras enviar al paciente</div>
</div>
<div class="detail-item">
<div class="dl">Registro de pago</div>
<div class="dv">Monto + método (efectivo, transferencia, tarjeta, EPS, cortesía)</div>
</div>
</div>
<div style="margin-top:.8rem">
<span class="view-badge">📄 recepcion.php</span>
<span class="api-badge">⚡ llamar_turno.php</span>
<span class="api-badge">⚡ create_solicitud.php</span>
<span class="api-badge">⚡ get_cola.php</span>
</div>
<div class="info-box green" style="margin-top:1rem">
<h4>📝 Guardar solicitud</h4>
<p>Al hacer clic en <strong>"Guardar solicitud"</strong>, se crea la <code>turnero_solicitud</code> con los exámenes (<code>turnero_examen_items</code>), el pago y el lugar destino. El sistema calcula automáticamente qué consentimientos informados se requieren basándose en los exámenes seleccionados y el lugar de destino.</p>
</div>
</div>
</div>
</div>
<!-- ── PASO 3: CONSENTIMIENTOS ── -->
<div class="flow-step" id="consentimientos">
<div class="flow-line">
<div class="flow-dot" style="background:linear-gradient(135deg,#ec4899,#db2777)">3</div>
<div class="flow-connector" style="--line-from:#ec4899;--line-to:#f59e0b"></div>
</div>
<div class="flow-content">
<div class="flow-card">
<h3>📱 Consentimientos Informados — Firma digital</h3>
<p>Una vez guardada la solicitud, aparecen los consentimientos requeridos. El recepcionista puede enviarlos por WhatsApp o hacer que el paciente firme presencialmente en una tablet.</p>
<div class="detail-grid">
<div class="detail-item">
<div class="dl">Cálculo automático</div>
<div class="dv">El sistema cruza <code>exam_tipo_consentimientos</code> + <code>turnero_lugar_consentimientos</code> para determinar qué formularios aplica</div>
</div>
<div class="detail-item">
<div class="dl">Token UUID</div>
<div class="dv">Cada consentimiento genera un token único (ej: "a3f2-4b1c-...") para acceso seguro sin login</div>
</div>
<div class="detail-item">
<div class="dl">Envío por WhatsApp</div>
<div class="dv">Se envía mensaje con template "consentimiento_turno" + enlace de firma al celular del paciente</div>
</div>
<div class="detail-item">
<div class="dl">Firma presencial</div>
<div class="dv">Botón "Firmar" abre modal con iframe del formulario — el paciente firma en la tablet del puesto</div>
</div>
</div>
<div style="margin-top:.8rem">
<span class="api-badge">⚡ send_consentimiento.php</span>
<span class="api-badge">⚡ get_consentimientos.php</span>
<span class="view-badge">📄 ver_formulario_enviado.php</span>
</div>
<div class="info-box purple" style="margin-top:1rem">
<h4>📊 Estados del consentimiento</h4>
<p>
<span style="color:#fde047">● pendiente</span> → creado pero no enviado &nbsp;|&nbsp;
<span style="color:#93c5fd">● enviado</span> → WhatsApp enviado &nbsp;|&nbsp;
<span style="color:#c084fc">● visto</span> → paciente abrió el enlace &nbsp;|&nbsp;
<span style="color:#86efac">● firmado</span> → paciente firmó &nbsp;|&nbsp;
<span style="color:#fca5a5">● rechazado</span> → paciente rechazó
</p>
</div>
<div class="info-box yellow" style="margin-top:.8rem">
<h4>🔄 Deduplicación inteligente</h4>
<p>Si varios exámenes requieren el <strong>mismo formulario</strong> de consentimiento, el sistema lo detecta y envía <strong>un solo enlace</strong> de firma. También si el paciente ya firmó ese consentimiento en una visita anterior, se reutiliza el token existente.</p>
</div>
</div>
</div>
</div>
<!-- ── PASO 4: PASAR A LUGAR ── -->
<div class="flow-step">
<div class="flow-line">
<div class="flow-dot" style="background:linear-gradient(135deg,#f59e0b,#d97706)">4</div>
<div class="flow-connector" style="--line-from:#f59e0b;--line-to:#22c55e"></div>
</div>
<div class="flow-content">
<div class="flow-card">
<h3>➡️ Pasar a Lugar — El paciente se desplaza</h3>
<p>El recepcionista hace clic en <strong>"Pasar a lugar"</strong>. El turno cambia de estado y aparece en la cola de la estación de toma de muestras asignada.</p>
<div class="detail-grid">
<div class="detail-item">
<div class="dl">Estado cambia a</div>
<div class="dv"><span style="color:#fcd34d;font-weight:700">en_espera_lugar</span></div>
</div>
<div class="detail-item">
<div class="dl">Se asigna</div>
<div class="dv"><code>lugar_destino_id</code> = la estación seleccionada (ej: "Toma de Muestras 1")</div>
</div>
<div class="detail-item">
<div class="dl">Pantalla TV</div>
<div class="dv">El display global muestra el anuncio: "A-001, pase a Toma de Muestras 1"</div>
</div>
<div class="detail-item">
<div class="dl">Recepción</div>
<div class="dv">La ficha se cierra y el recepcionista puede llamar al siguiente turno</div>
</div>
</div>
<div style="margin-top:.8rem">
<span class="api-badge">⚡ cambiar_estado.php</span>
</div>
</div>
</div>
</div>
<!-- ── PASO 5: TOMA DE MUESTRAS ── -->
<div class="flow-step" id="muestras">
<div class="flow-line">
<div class="flow-dot" style="background:linear-gradient(135deg,#22c55e,#16a34a)">5</div>
<div class="flow-connector" style="--line-from:#22c55e;--line-to:#06b6d4"></div>
</div>
<div class="flow-content">
<div class="flow-card" style="border-color:rgba(34,197,94,.2)">
<h3>🔬 Toma de Muestras — Proceso completo</h3>
<p>El auxiliar de la estación de muestras ve en su pantalla la cola de pacientes asignados a su lugar. Este es el <strong>proceso detallado de toma de muestras</strong>:</p>
<!-- Sub-flujo detallado -->
<div style="margin:1rem 0">
<div style="display:flex;align-items:flex-start;gap:1rem;margin-bottom:1rem;padding:1rem;background:rgba(34,197,94,.06);border-radius:12px;border:1px solid rgba(34,197,94,.12)">
<div style="width:36px;height:36px;border-radius:50%;background:#22c55e;display:flex;align-items:center;justify-content:center;font-weight:800;color:#fff;flex-shrink:0;font-size:.85rem">5a</div>
<div>
<h4 style="font-size:.92rem;font-weight:700;color:#86efac;margin-bottom:.3rem">Llamar paciente</h4>
<p style="font-size:.82rem;color:#94a3b8">El auxiliar hace clic en <strong>"Llamar siguiente"</strong>. El motor de cola selecciona el turno con mayor prioridad (menor peso) y mayor tiempo de espera. El turno pasa a estado <span style="color:#86efac;font-weight:700">en_servicio</span>.</p>
<p style="font-size:.78rem;color:#64748b;margin-top:.3rem">⚠️ <strong>Bloqueo:</strong> Si hay consentimientos sin firmar, el botón "Iniciar atención" está deshabilitado y se muestra un banner rojo indicando cuántos consentimientos faltan.</p>
</div>
</div>
<div style="display:flex;align-items:flex-start;gap:1rem;margin-bottom:1rem;padding:1rem;background:rgba(34,197,94,.06);border-radius:12px;border:1px solid rgba(34,197,94,.12)">
<div style="width:36px;height:36px;border-radius:50%;background:#22c55e;display:flex;align-items:center;justify-content:center;font-weight:800;color:#fff;flex-shrink:0;font-size:.85rem">5b</div>
<div>
<h4 style="font-size:.92rem;font-weight:700;color:#86efac;margin-bottom:.3rem">Verificar ficha del paciente</h4>
<p style="font-size:.82rem;color:#94a3b8">Se abre la ficha con toda la información:</p>
<ul style="font-size:.82rem;color:#94a3b8;margin-top:.3rem;padding-left:1.2rem">
<li><strong>Datos del paciente:</strong> nombre, documento, fecha de nacimiento, celular</li>
<li><strong>Exámenes solicitados:</strong> lista con código y nombre de cada examen</li>
<li><strong>Consentimientos:</strong> estado de cada uno (firmado/rechazado/pendiente)</li>
</ul>
</div>
</div>
<div style="display:flex;align-items:flex-start;gap:1rem;margin-bottom:1rem;padding:1rem;background:rgba(34,197,94,.06);border-radius:12px;border:1px solid rgba(34,197,94,.12)">
<div style="width:36px;height:36px;border-radius:50%;background:#22c55e;display:flex;align-items:center;justify-content:center;font-weight:800;color:#fff;flex-shrink:0;font-size:.85rem">5c</div>
<div>
<h4 style="font-size:.92rem;font-weight:700;color:#86efac;margin-bottom:.3rem">Resolver consentimientos pendientes (si los hay)</h4>
<p style="font-size:.82rem;color:#94a3b8">Si algún consentimiento aún no está firmado, el auxiliar puede:</p>
<ul style="font-size:.82rem;color:#94a3b8;margin-top:.3rem;padding-left:1.2rem">
<li><strong>"Firmar aquí":</strong> Abre un modal con el formulario — el paciente firma en la tablet del puesto</li>
<li><strong>"WA":</strong> Reenvía el enlace de firma por WhatsApp al celular del paciente</li>
<li><strong>Polling automático:</strong> Cada 5 segundos el sistema verifica si el paciente firmó desde su celular</li>
</ul>
</div>
</div>
<div style="display:flex;align-items:flex-start;gap:1rem;margin-bottom:1rem;padding:1rem;background:rgba(34,197,94,.06);border-radius:12px;border:1px solid rgba(34,197,94,.12)">
<div style="width:36px;height:36px;border-radius:50%;background:#16a34a;display:flex;align-items:center;justify-content:center;font-weight:800;color:#fff;flex-shrink:0;font-size:.85rem">5d</div>
<div>
<h4 style="font-size:.92rem;font-weight:700;color:#86efac;margin-bottom:.3rem">Iniciar atención</h4>
<p style="font-size:.82rem;color:#94a3b8">Cuando todos los consentimientos están OK (firmados o rechazados), el botón <strong>"Iniciar atención"</strong> se habilita. El auxiliar hace clic y el estado cambia a <span style="color:#86efac;font-weight:700">en_servicio</span>. Se registra <code>inicio_lugar_at</code>.</p>
<p style="font-size:.78rem;color:#64748b;margin-top:.3rem">🧪 <strong>En este momento el auxiliar toma la muestra de sangre/orina/etc. al paciente.</strong></p>
</div>
</div>
<div style="display:flex;align-items:flex-start;gap:1rem;margin-bottom:1rem;padding:1rem;background:rgba(34,197,94,.06);border-radius:12px;border:1px solid rgba(34,197,94,.12)">
<div style="width:36px;height:36px;border-radius:50%;background:#15803d;display:flex;align-items:center;justify-content:center;font-weight:800;color:#fff;flex-shrink:0;font-size:.85rem">5e</div>
<div>
<h4 style="font-size:.92rem;font-weight:700;color:#86efac;margin-bottom:.3rem">Finalizar atención</h4>
<p style="font-size:.82rem;color:#94a3b8">El auxiliar hace clic en <strong>"Finalizar"</strong>. El turno pasa a estado <span style="color:#cbd5e1;font-weight:700">finalizado</span>. Se registra <code>fin_lugar_at</code>. La ficha se cierra y el auxiliar puede llamar al siguiente paciente.</p>
</div>
</div>
<div style="display:flex;align-items:flex-start;gap:1rem;padding:1rem;background:rgba(239,68,68,.06);border-radius:12px;border:1px solid rgba(239,68,68,.12)">
<div style="width:36px;height:36px;border-radius:50%;background:#ef4444;display:flex;align-items:center;justify-content:center;font-weight:800;color:#fff;flex-shrink:0;font-size:.85rem">⚠️</div>
<div>
<h4 style="font-size:.92rem;font-weight:700;color:#fca5a5;margin-bottom:.3rem">Casos especiales</h4>
<p style="font-size:.82rem;color:#94a3b8">
<strong>"Ausente":</strong> El paciente no se presentó → estado <span style="color:#fca5a5">ausente</span><br>
<strong>"Regresar a cola":</strong> El paciente necesita esperar (ej: ayuno insuficiente) → vuelve a <span style="color:#fcd34d">en_espera_lugar</span>
</p>
</div>
</div>
</div>
<div style="margin-top:.8rem">
<span class="view-badge">📄 lugar.php</span>
<span class="api-badge">⚡ llamar_turno.php</span>
<span class="api-badge">⚡ cambiar_estado.php</span>
<span class="api-badge">⚡ get_consentimientos.php</span>
</div>
</div>
</div>
</div>
<!-- ── PASO 6: PANTALLAS TV ── -->
<div class="flow-step" id="pantallas">
<div class="flow-line">
<div class="flow-dot" style="background:linear-gradient(135deg,#06b6d4,#0891b2)">6</div>
<div class="flow-connector" style="--line-from:#06b6d4;--line-to:#6366f1"></div>
</div>
<div class="flow-content">
<div class="flow-card">
<h3>📺 Pantallas TV — Información en tiempo real</h3>
<p>Durante todo el proceso, las pantallas TV muestran el estado actualizado del sistema. Hay 3 tipos de display:</p>
<div class="detail-grid">
<div class="detail-item">
<div class="dl">Display Global</div>
<div class="dv">Vista general con todas las áreas: recepción + todas las estaciones de muestras. Tema oscuro. Anuncio animado cuando se llama un turno. Video de fondo configurable.</div>
</div>
<div class="detail-item">
<div class="dl">Display Recepción</div>
<div class="dv">Muestra el turno activo en un escritorio de recepción específico + cola de espera. Se actualiza vía SSE.</div>
</div>
<div class="detail-item">
<div class="dl">Display por Lugar</div>
<div class="dv">Muestra el turno activo en una estación de muestras específica + cola de ese lugar.</div>
</div>
<div class="detail-item">
<div class="dl">Actualización</div>
<div class="dv"><strong>SSE</strong> (Server-Sent Events) cada 2s si hay cambios + <strong>polling</strong> de respaldo cada 4s. Beep + voz sintética al llamar turno.</div>
</div>
</div>
<div style="margin-top:.8rem">
<span class="view-badge">📄 display_global.php</span>
<span class="view-badge">📄 display.php</span>
<span class="view-badge">📄 display_recepcion.php</span>
<span class="api-badge">⚡ sse_turno.php</span>
<span class="api-badge">⚡ get_cola.php</span>
<span class="api-badge">⚡ get_display_global.php</span>
</div>
</div>
</div>
</div>
<!-- ── PASO 7: ADMIN ── -->
<div class="flow-step" id="admin">
<div class="flow-line">
<div class="flow-dot" style="background:linear-gradient(135deg,#6366f1,#4f46e5)">7</div>
<div class="flow-connector" style="--line-from:#6366f1;--line-to:#6366f1"></div>
</div>
<div class="flow-content">
<div class="flow-card">
<h3>⚙️ Administración — Monitoreo y configuración</h3>
<p>El administrador tiene acceso a vistas de control y configuración del sistema completo.</p>
<div class="detail-grid">
<div class="detail-item">
<div class="dl">Dashboard</div>
<div class="dv">KPIs del día: total turnos, atendidos, en espera, ausentes, tiempo promedio de espera y servicio. Desglose por prioridad y lugar. Auto-refresh cada 30s.</div>
</div>
<div class="detail-item">
<div class="dl">Historial</div>
<div class="dv">Consulta multi-día con filtros por estado, lugar, prioridad y texto libre. Paginación. Detalle expandible con todos los timestamps.</div>
</div>
<div class="detail-item">
<div class="dl">Configuración</div>
<div class="dv">CRUD de lugares (recepción/muestras), tipos de examen, prioridades, sesiones, WhatsApp (template Meta), video TV.</div>
</div>
<div class="detail-item">
<div class="dl">Exportar</div>
<div class="dv">CSV con todos los turnos, timestamps, exámenes, pagos y estado de consentimientos.</div>
</div>
</div>
<div style="margin-top:.8rem">
<span class="view-badge">📄 dashboard.php</span>
<span class="view-badge">📄 historial.php</span>
<span class="view-badge">📄 configuracion.php</span>
<span class="api-badge">⚡ get_dashboard.php</span>
<span class="api-badge">⚡ export_csv.php</span>
<span class="api-badge">⚡ get_historial.php</span>
<span class="api-badge">⚡ export_historial.php</span>
</div>
</div>
</div>
</div>
</div><!-- /flow -->
</div>
<!-- ════════════════════════════════════════════════════════════ -->
<!-- DIAGRAMA DE SECUENCIA -->
<!-- ════════════════════════════════════════════════════════════ -->
<div class="section" id="secuencia">
<div class="section-title">
<div class="num" style="background:linear-gradient(135deg,#f59e0b,#ef4444)">📡</div>
<div>
<h2>Diagrama de Secuencia — APIs y Datos</h2>
<span class="tag" style="background:rgba(245,158,11,.15);color:#fbbf24">Comunicaciones</span>
</div>
</div>
<div class="seq-diagram">
<table class="seq-table">
<thead>
<tr>
<th style="text-align:left;width:40px">#</th>
<th>Actor / Vista</th>
<th>→ API</th>
<th>Acción en BD</th>
<th>Resultado</th>
<th>Notifica</th>
</tr>
</thead>
<tbody>
<tr>
<td style="color:#60a5fa;font-weight:700">1</td>
<td style="text-align:left">Kiosko</td>
<td><span class="seq-arrow req">POST create_turno.php</span></td>
<td style="font-size:.72rem;color:#94a3b8">INSERT turnero_turnos (estado=espera, correlativo por prioridad)</td>
<td style="font-size:.72rem">Código "A-001" + posición cola</td>
<td><span class="seq-arrow sse">SSE notify</span></td>
</tr>
<tr>
<td style="color:#60a5fa;font-weight:700">2</td>
<td style="text-align:left">Recepción</td>
<td><span class="seq-arrow req">POST llamar_turno.php</span></td>
<td style="font-size:.72rem;color:#94a3b8">UPDATE estado=en_recepcion + timestamps</td>
<td style="font-size:.72rem">Turno con datos completos</td>
<td><span class="seq-arrow sse">SSE notify</span></td>
</tr>
<tr>
<td style="color:#60a5fa;font-weight:700">3</td>
<td style="text-align:left">Recepción</td>
<td><span class="seq-arrow req">POST create_solicitud.php</span></td>
<td style="font-size:.72rem;color:#94a3b8">INSERT turnero_solicitudes + turnero_examen_items</td>
<td style="font-size:.72rem">Solicitud + consentimientos requeridos</td>
<td><span class="seq-arrow sse">SSE notify</span></td>
</tr>
<tr>
<td style="color:#60a5fa;font-weight:700">4</td>
<td style="text-align:left">Recepción</td>
<td><span class="seq-arrow req">POST send_consentimiento.php</span></td>
<td style="font-size:.72rem;color:#94a3b8">INSERT/UPDATE turnero_consentimientos + tokens UUID</td>
<td style="font-size:.72rem">Estado → enviado</td>
<td><span class="seq-arrow wa">WhatsApp</span></td>
</tr>
<tr>
<td style="color:#60a5fa;font-weight:700">5</td>
<td style="text-align:left">Paciente (celular)</td>
<td><span class="seq-arrow res">GET ver_formulario_enviado.php?token=...</span></td>
<td style="font-size:.72rem;color:#94a3b8">UPDATE turnero_consentimientos estado=firmado</td>
<td style="font-size:.72rem">Formulario firmado + evidencia</td>
<td><span class="seq-arrow res">postMessage</span></td>
</tr>
<tr>
<td style="color:#60a5fa;font-weight:700">6</td>
<td style="text-align:left">Recepción</td>
<td><span class="seq-arrow req">POST cambiar_estado.php → en_espera_lugar</span></td>
<td style="font-size:.72rem;color:#94a3b8">UPDATE estado + lugar_destino_id</td>
<td style="font-size:.72rem">Turno pasa a cola del lugar</td>
<td><span class="seq-arrow sse">SSE notify</span></td>
</tr>
<tr>
<td style="color:#60a5fa;font-weight:700">7</td>
<td style="text-align:left">Lugar (auxiliar)</td>
<td><span class="seq-arrow req">POST llamar_turno.php (area=lugar)</span></td>
<td style="font-size:.72rem;color:#94a3b8">UPDATE estado=en_servicio + timestamps</td>
<td style="font-size:.72rem">Turno con ficha completa</td>
<td><span class="seq-arrow sse">SSE notify</span></td>
</tr>
<tr>
<td style="color:#60a5fa;font-weight:700">8</td>
<td style="text-align:left">Lugar (auxiliar)</td>
<td><span class="seq-arrow req">GET get_consentimientos.php (polling 5s)</span></td>
<td style="font-size:.72rem;color:#94a3b8">SELECT turnero_consentimientos + paciente + exámenes</td>
<td style="font-size:.72rem">Verifica si todos están firmados</td>
<td></td>
</tr>
<tr>
<td style="color:#60a5fa;font-weight:700">9</td>
<td style="text-align:left">Lugar (auxiliar)</td>
<td><span class="seq-arrow req">POST cambiar_estado.php → finalizado</span></td>
<td style="font-size:.72rem;color:#94a3b8">UPDATE estado + fin_lugar_at</td>
<td style="font-size:.72rem">Turno completado</td>
<td><span class="seq-arrow sse">SSE notify</span></td>
</tr>
<tr>
<td style="color:#60a5fa;font-weight:700">10</td>
<td style="text-align:left">Pantallas TV</td>
<td><span class="seq-arrow sse">GET sse_turno.php (conexión persistente)</span></td>
<td style="font-size:.72rem;color:#94a3b8">SELECT cola + activo (cada 2s o por cambio)</td>
<td style="font-size:.72rem">Render en tiempo real + audio + voz</td>
<td></td>
</tr>
</tbody>
</table>
</div>
</div>
<!-- ════════════════════════════════════════════════════════════ -->
<!-- TABLAS DE BASE DE DATOS -->
<!-- ════════════════════════════════════════════════════════════ -->
<div class="section" id="bd">
<div class="section-title">
<div class="num" style="background:linear-gradient(135deg,#0ea5e9,#0284c7)">🗄️</div>
<div>
<h2>Tablas de Base de Datos</h2>
<span class="tag" style="background:rgba(14,165,233,.15);color:#38bdf8">9 tablas del turnero</span>
</div>
</div>
<div style="display:grid;grid-template-columns:repeat(auto-fill,minmax(340px,1fr));gap:1rem">
<div style="background:rgba(255,255,255,.04);border:1px solid rgba(255,255,255,.08);border-radius:14px;padding:1.2rem">
<h4 style="font-size:.88rem;font-weight:700;color:#60a5fa;margin-bottom:.5rem">📋 turnero_turnos</h4>
<p style="font-size:.78rem;color:#94a3b8">Tabla principal. Cada fila = un turno. Almacena: código, número, prioridad, estado, timestamps de cada fase (creado, llamado recepción, inicio/fin recepción, llamado lugar, inicio/fin lugar), paciente_nombre (cédula del kiosko), paciente_cel.</p>
</div>
<div style="background:rgba(255,255,255,.04);border:1px solid rgba(255,255,255,.08);border-radius:14px;padding:1.2rem">
<h4 style="font-size:.88rem;font-weight:700;color:#60a5fa;margin-bottom:.5rem">📅 turnero_sesiones</h4>
<p style="font-size:.78rem;color:#94a3b8">Una por día. Controla apertura/cierre del sistema. Almacena: fecha, abierto_por, inicio_at, fin_at, sse_ping_at (para notificaciones SSE).</p>
</div>
<div style="background:rgba(255,255,255,.04);border:1px solid rgba(255,255,255,.08);border-radius:14px;padding:1.2rem">
<h4 style="font-size:.88rem;font-weight:700;color:#60a5fa;margin-bottom:.5rem">📝 turnero_solicitudes</h4>
<p style="font-size:.78rem;color:#94a3b8">Vincula un turno con un paciente real, un lugar destino y los datos de pago. Se crea en recepción después de llamar el turno.</p>
</div>
<div style="background:rgba(255,255,255,.04);border:1px solid rgba(255,255,255,.08);border-radius:14px;padding:1.2rem">
<h4 style="font-size:.88rem;font-weight:700;color:#60a5fa;margin-bottom:.5rem">🧪 turnero_examen_items</h4>
<p style="font-size:.78rem;color:#94a3b8">Relación N:M entre solicitud y tipos de examen. Cada fila = un examen solicitado para esa solicitud.</p>
</div>
<div style="background:rgba(255,255,255,.04);border:1px solid rgba(255,255,255,.08);border-radius:14px;padding:1.2rem">
<h4 style="font-size:.88rem;font-weight:700;color:#60a5fa;margin-bottom:.5rem">✍️ turnero_consentimientos</h4>
<p style="font-size:.78rem;color:#94a3b8">Cada consentimiento requerido por turno. Almacena: token UUID, estado (pendiente/enviado/visto/firmado/rechazado), timestamps.</p>
</div>
<div style="background:rgba(255,255,255,.04);border:1px solid rgba(255,255,255,.08);border-radius:14px;padding:1.2rem">
<h4 style="font-size:.88rem;font-weight:700;color:#60a5fa;margin-bottom:.5rem">📍 turnero_lugares</h4>
<p style="font-size:.78rem;color:#94a3b8">Catálogo de estaciones: recepción, toma de muestras 1, 2, etc. Campos: nombre, tipo (recepcion/muestras), descripción, sort_order, activo.</p>
</div>
<div style="background:rgba(255,255,255,.04);border:1px solid rgba(255,255,255,.08);border-radius:14px;padding:1.2rem">
<h4 style="font-size:.88rem;font-weight:700;color:#60a5fa;margin-bottom:.5rem">🎫 turnero_prioridades</h4>
<p style="font-size:.78rem;color:#94a3b8">Código (A-F), nombre, color, icono, descripción, orden_peso. Configurable desde el panel admin.</p>
</div>
<div style="background:rgba(255,255,255,.04);border:1px solid rgba(255,255,255,.08);border-radius:14px;padding:1.2rem">
<h4 style="font-size:.88rem;font-weight:700;color:#60a5fa;margin-bottom:.5rem">🧪 exam_tipos</h4>
<p style="font-size:.78rem;color:#94a3b8">Catálogo de tipos de examen del laboratorio. Código, nombre, categoría. Se usa en la ficha de recepción.</p>
</div>
<div style="background:rgba(255,255,255,.04);border:1px solid rgba(255,255,255,.08);border-radius:14px;padding:1.2rem">
<h4 style="font-size:.88rem;font-weight:700;color:#60a5fa;margin-bottom:.5rem">🔗 Tablas de relación</h4>
<p style="font-size:.78rem;color:#94a3b8">
<strong>exam_tipo_consentimientos:</strong> qué formularios requiere cada tipo de examen.<br>
<strong>turnero_lugar_consentimientos:</strong> qué formularios requiere cada lugar/servicio.
</p>
</div>
</div>
</div>
<!-- ════════════════════════════════════════════════════════════ -->
<!-- RESUMEN VISUAL -->
<!-- ════════════════════════════════════════════════════════════ -->
<div class="section">
<div class="section-title">
<div class="num" style="background:linear-gradient(135deg,#8b5cf6,#6d28d9)">🗺️</div>
<div>
<h2>Resumen Visual del Flujo</h2>
<span class="tag" style="background:rgba(139,92,246,.15);color:#a78bfa">Big picture</span>
</div>
</div>
<div style="background:rgba(255,255,255,.03);border:1px solid rgba(255,255,255,.08);border-radius:20px;padding:2rem;overflow-x:auto">
<div style="display:flex;align-items:center;justify-content:center;gap:.5rem;flex-wrap:wrap;min-width:700px">
<!-- Step boxes -->
<div style="text-align:center;padding:1rem;background:rgba(59,130,246,.12);border:2px solid rgba(59,130,246,.3);border-radius:16px;min-width:120px">
<div style="font-size:1.8rem">🖥️</div>
<div style="font-size:.78rem;font-weight:700;color:#60a5fa;margin-top:.3rem">KIOSKO</div>
<div style="font-size:.65rem;color:#94a3b8">Paciente obtiene<br>turno A-001</div>
<div style="font-size:.6rem;color:#fde047;margin-top:.3rem">estado: espera<br>(correlativo por prioridad)</div>
</div>
<div style="font-size:1.5rem;color:#475569"></div>
<div style="text-align:center;padding:1rem;background:rgba(139,92,246,.12);border:2px solid rgba(139,92,246,.3);border-radius:16px;min-width:120px">
<div style="font-size:1.8rem">🛎️</div>
<div style="font-size:.78rem;font-weight:700;color:#a78bfa;margin-top:.3rem">RECEPCIÓN</div>
<div style="font-size:.65rem;color:#94a3b8">Vincula paciente<br>exámenes + pago</div>
<div style="font-size:.6rem;color:#93c5fd;margin-top:.3rem">estado: en_recepcion</div>
</div>
<div style="font-size:1.5rem;color:#475569"></div>
<div style="text-align:center;padding:1rem;background:rgba(236,72,153,.12);border:2px solid rgba(236,72,153,.3);border-radius:16px;min-width:120px">
<div style="font-size:1.8rem">📱</div>
<div style="font-size:.78rem;font-weight:700;color:#f472b6;margin-top:.3rem">CONSENTIM.</div>
<div style="font-size:.65rem;color:#94a3b8">Envío WhatsApp<br>firma digital</div>
<div style="font-size:.6rem;color:#c084fc;margin-top:.3rem">tokens UUID</div>
</div>
<div style="font-size:1.5rem;color:#475569"></div>
<div style="text-align:center;padding:1rem;background:rgba(245,158,11,.12);border:2px solid rgba(245,158,11,.3);border-radius:16px;min-width:120px">
<div style="font-size:1.8rem">🚶</div>
<div style="font-size:.78rem;font-weight:700;color:#fbbf24;margin-top:.3rem">TRÁNSITO</div>
<div style="font-size:.65rem;color:#94a3b8">Paciente se<br>desplaza</div>
<div style="font-size:.6rem;color:#fcd34d;margin-top:.3rem">estado: en_espera_lugar</div>
</div>
<div style="font-size:1.5rem;color:#475569"></div>
<div style="text-align:center;padding:1rem;background:rgba(34,197,94,.12);border:2px solid rgba(34,197,94,.3);border-radius:16px;min-width:120px">
<div style="font-size:1.8rem">🔬</div>
<div style="font-size:.78rem;font-weight:700;color:#4ade80;margin-top:.3rem">MUESTRAS</div>
<div style="font-size:.65rem;color:#94a3b8">Verifica + toma<br>muestra</div>
<div style="font-size:.6rem;color:#86efac;margin-top:.3rem">estado: en_servicio</div>
</div>
<div style="font-size:1.5rem;color:#475569"></div>
<div style="text-align:center;padding:1rem;background:rgba(203,213,225,.12);border:2px solid rgba(203,213,225,.3);border-radius:16px;min-width:120px">
<div style="font-size:1.8rem"></div>
<div style="font-size:.78rem;font-weight:700;color:#cbd5e1;margin-top:.3rem">FINALIZADO</div>
<div style="font-size:.65rem;color:#94a3b8">Muestra tomada<br>turno completo</div>
<div style="font-size:.6rem;color:#94a3b8;margin-top:.3rem">estado: finalizado</div>
</div>
</div>
<!-- SSE line -->
<div style="text-align:center;margin-top:1.5rem;padding:.8rem;background:rgba(251,191,36,.06);border:1px dashed rgba(251,191,36,.3);border-radius:12px">
<span style="font-size:.82rem;color:#fbbf24;font-weight:700">📡 SSE (Server-Sent Events)</span>
<span style="font-size:.78rem;color:#94a3b8"> — Cada cambio de estado notifica a todas las pantallas TV en tiempo real (beep + voz sintética + animación)</span>
</div>
</div>
</div>
</div><!-- /container -->
<!-- ════════════════════════════════════════════════════════════ -->
<!-- FOOTER -->
<!-- ════════════════════════════════════════════════════════════ -->
<div class="footer">
<p>Sistema Turnero Inteligente — Laboratorio Clínico — Documentación de flujo completo</p>
<p style="margin-top:.3rem">Módulo: <code>modules/turnero/</code> · 22 APIs · 9 Vistas · 9 Tablas BD · SSE + WhatsApp Integration</p>
</div>
</body>
</html>