feat: exámenes en recepción — lista vertical + búsqueda por CUPS

- Items seleccionados se muestran en filas verticales con chip de CUPS/código
- Búsqueda por CUPS funciona (código CUPS incluido en texto de <option>)
- Dropdown muestra chip de CUPS junto al nombre del examen
- PHP SELECT incluye columna cups de exam_tipos

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Lizandro Guarnizo
2026-07-16 19:15:24 -05:00
co-authored by Claude Sonnet 4.6
parent 7a4413e173
commit c22c38a58f
+37 -6
View File
@@ -82,7 +82,7 @@ try {
$lugarMuestras = $lugaresDestino[0] ?? null;
$examenes = $pdo->query(
"SELECT et.id, et.codigo, et.nombre, et.categoria,
"SELECT et.id, et.codigo, et.nombre, et.categoria, et.cups,
IF(COUNT(etc.formulario_id) > 0, 1, 0) AS tiene_consentimiento
FROM exam_tipos et
LEFT JOIN exam_tipo_consentimientos etc ON etc.exam_tipo_id = et.id
@@ -235,12 +235,36 @@ $adminNombre = $_SESSION['admin_user']['full_name'] ?? $_SESSION['admin_user']['
/* ── Tom Select — exámenes ── */
#wrap-examenes .ts-wrapper { font-size: .88rem; }
#wrap-examenes .ts-control { min-height: 44px; border-radius: 8px; }
#wrap-examenes .ts-control {
min-height: 44px; border-radius: 8px;
flex-direction: column; align-items: stretch; gap: 3px;
}
#wrap-examenes .ts-control input { order: -1; }
#wrap-examenes .ts-dropdown { font-size: .88rem; }
#wrap-examenes .ts-dropdown .optgroup-header {
font-size: .7rem; font-weight: 700; color: #94a3b8;
text-transform: uppercase; letter-spacing: .08em;
}
/* Item seleccionado — fila completa */
#wrap-examenes .ts-control .item {
display: flex; align-items: center; width: 100%;
background: #eff6ff; color: #1e40af;
border: 1px solid #bfdbfe; border-radius: 6px;
padding: 3px 6px; gap: 6px; box-sizing: border-box;
}
#wrap-examenes .ts-control .item .ts-exam-cups {
font-size: .65rem; font-weight: 700; color: #64748b;
background: #e2e8f0; border-radius: 3px; padding: 0 4px;
white-space: nowrap; flex-shrink: 0;
}
#wrap-examenes .ts-control .item .ts-exam-nom { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
#wrap-examenes .ts-control .item .remove { margin-left: auto; flex-shrink: 0; color: #93c5fd; }
/* Dropdown: CUPS chip al lado del nombre */
#wrap-examenes .ts-dropdown .ts-exam-cups-drop {
font-size: .65rem; font-weight: 600; color: #64748b;
background: #f1f5f9; border-radius: 3px; padding: 0 4px;
white-space: nowrap; margin-left: 4px;
}
.ts-consent-badge {
font-size: .65rem; font-weight: 700; border-radius: 4px;
padding: 1px 5px; margin-left: 5px;
@@ -659,8 +683,10 @@ document.addEventListener('DOMContentLoaded', function() {
<optgroup label="<?= htmlspecialchars($cat) ?>">
<?php foreach ($items as $ex): ?>
<option value="<?= (int)$ex['id'] ?>"
data-consent="<?= (int)$ex['tiene_consentimiento'] ?>">
<?= htmlspecialchars($ex['codigo']) ?> — <?= htmlspecialchars($ex['nombre']) ?>
data-consent="<?= (int)$ex['tiene_consentimiento'] ?>"
data-cups="<?= htmlspecialchars($ex['cups'] ?? '') ?>"
data-codigo="<?= htmlspecialchars($ex['codigo'] ?? '') ?>">
<?= htmlspecialchars($ex['codigo']) ?> — <?= htmlspecialchars($ex['nombre']) ?><?= $ex['cups'] ? ' ' . htmlspecialchars($ex['cups']) : '' ?>
</option>
<?php endforeach; ?>
</optgroup>
@@ -1050,6 +1076,7 @@ document.addEventListener('DOMContentLoaded', () => {
plugins: ['remove_button'],
maxOptions: null,
selectOnTab: true,
onItemAdd() { recalcularPrecios(); examTS.setTextboxValue(''); examTS.refreshOptions(false); },
onItemRemove: () => recalcularPrecios(),
onType(str) {
@@ -1062,14 +1089,18 @@ document.addEventListener('DOMContentLoaded', () => {
render: {
option(data, escape) {
const consent = data['data-consent'] == '1' || data.consent == '1';
const cups = data['data-cups'] || data.cups || '';
return `<div class="d-flex align-items-center justify-content-between">
<span>${escape(data.text)}</span>
<span>${escape(data.text)}${cups ? `<span class="ts-exam-cups-drop">${escape(cups)}</span>` : ''}</span>
${consent ? '<span class="ts-consent-badge"><i class="fas fa-file-signature me-1"></i>Consentimiento</span>' : ''}
</div>`;
},
item(data, escape) {
const consent = data['data-consent'] == '1' || data.consent == '1';
return `<div>${escape(data.text)}${consent ? '<span class="ts-consent-dot" title="Requiere consentimiento"> ⚠</span>' : ''}</div>`;
const cups = data['data-cups'] || data.cups || '';
const codigo = data['data-codigo'] || data.codigo || '';
const label = cups || codigo;
return `<div class="ts-exam-nom">${label ? `<span class="ts-exam-cups">${escape(label)}</span>` : ''}${escape(data.text)}${consent ? '<span class="ts-consent-dot" title="Requiere consentimiento"> ⚠</span>' : ''}</div>`;
}
}
});