feat: store and display per-patient sync detail in historial
- Add detalle_json column to sync_wa_log via migration - guardar_sync_log() now stores doc, nombre, action, examenes count per patient - Historial table shows "Ver N" button that expands a row with patient list, action badge (Nuevo/Actualizado/Omitido) and exam count per patient Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 4.6
parent
29b6aa4016
commit
e044f21e3e
@@ -158,6 +158,11 @@ function switchTab(name) {
|
||||
const _origenLabel = { manual: 'Manual', tercero: 'Tercero', scheduler: 'Scheduler', automation: 'Automatización' };
|
||||
const _modoLabel = { insertar: 'Solo nuevos', upsert: 'Upsert' };
|
||||
|
||||
function toggleDet(id) {
|
||||
const row = document.getElementById(id);
|
||||
if (row) row.classList.toggle('hidden');
|
||||
}
|
||||
|
||||
async function ejecutarSyncNow() {
|
||||
const btn = document.getElementById('btn-sync-now');
|
||||
const ventana = document.getElementById('sel-ventana').value;
|
||||
@@ -235,34 +240,58 @@ async function cargarHistorialScheduler() {
|
||||
<thead>
|
||||
<tr class="text-left text-xs text-gray-500 border-b border-gray-100">
|
||||
<th class="pb-2 pr-4 font-medium">Fecha</th>
|
||||
<th class="pb-2 pr-3 font-medium">Usuario</th>
|
||||
<th class="pb-2 pr-3 font-medium text-right">Procesados</th>
|
||||
<th class="pb-2 pr-3 font-medium text-right text-blue-600">Nuevos</th>
|
||||
<th class="pb-2 pr-3 font-medium text-right text-gray-400">Omitidos</th>
|
||||
<th class="pb-2 pr-3 font-medium text-right text-green-600">Actualizados</th>
|
||||
<th class="pb-2 pr-3 font-medium text-right text-red-500">Errores</th>
|
||||
<th class="pb-2 font-medium text-right">Detalle</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="divide-y divide-gray-50">
|
||||
${sched.map(r => {
|
||||
${sched.map((r, i) => {
|
||||
const fecha = r.created_at.replace('T', ' ').slice(0, 16);
|
||||
const errDet = r.errores_det ? JSON.parse(r.errores_det) : [];
|
||||
const pacientes = r.detalle_json ? JSON.parse(r.detalle_json) : [];
|
||||
const rowId = 'hist-det-' + i;
|
||||
return `
|
||||
<tr class="hover:bg-gray-50">
|
||||
<td class="py-2 pr-4 text-gray-500 whitespace-nowrap font-mono text-xs">${fecha}</td>
|
||||
<td class="py-2 pr-3 text-xs text-gray-400">${r.username || '—'}</td>
|
||||
<td class="py-2 pr-3 text-right font-medium">${r.total}</td>
|
||||
<td class="py-2 pr-3 text-right text-blue-600 font-medium">${r.created}</td>
|
||||
<td class="py-2 pr-3 text-right text-gray-400">${r.skipped}</td>
|
||||
<td class="py-2 pr-3 text-right text-green-600">${r.updated}</td>
|
||||
<td class="py-2 pr-3 text-right ${r.errores > 0 ? 'text-red-500 font-medium' : 'text-gray-300'}">
|
||||
${r.errores > 0 && errDet.length ? `
|
||||
<details>
|
||||
<summary class="cursor-pointer">${r.errores}</summary>
|
||||
<div class="absolute z-10 bg-white border border-red-100 rounded-lg shadow-lg p-2 text-xs mt-1 max-w-xs">
|
||||
${errDet.map(e => `<div class="text-red-600">${e.doc} — ${e.msg}</div>`).join('')}
|
||||
</div>
|
||||
</details>` : r.errores || '—'}
|
||||
${r.errores > 0 && errDet.length
|
||||
? errDet.map(e => `<div class="text-red-600 text-xs">${e.doc} — ${e.msg}</div>`).join('')
|
||||
: r.errores || '—'}
|
||||
</td>
|
||||
</tr>`;
|
||||
<td class="py-2 text-right">
|
||||
${pacientes.length ? `
|
||||
<button onclick="toggleDet('${rowId}')"
|
||||
class="text-xs text-blue-500 hover:underline">Ver ${pacientes.length}</button>` : '—'}
|
||||
</td>
|
||||
</tr>
|
||||
${pacientes.length ? `
|
||||
<tr id="${rowId}" class="hidden bg-gray-50">
|
||||
<td colspan="7" class="px-4 pb-3 pt-1">
|
||||
<div class="text-xs text-gray-500 space-y-0.5 max-h-48 overflow-y-auto">
|
||||
${pacientes.map(p => {
|
||||
const badge = p.action === 'created' ? '<span class="text-blue-600">Nuevo</span>'
|
||||
: p.action === 'updated' ? '<span class="text-green-600">Actualizado</span>'
|
||||
: p.action === 'skipped' ? '<span class="text-gray-400">Omitido</span>'
|
||||
: `<span class="text-red-500">${p.action}</span>`;
|
||||
const exBadge = p.examenes > 0
|
||||
? `<span class="text-purple-500">${p.examenes} exám.</span>` : '';
|
||||
return `<div class="flex items-center gap-3 py-0.5 border-b border-gray-100">
|
||||
<span class="text-gray-400 w-28 shrink-0">${p.doc}</span>
|
||||
<span class="flex-1 truncate">${p.nombre}</span>
|
||||
${badge} ${exBadge}
|
||||
</div>`;
|
||||
}).join('')}
|
||||
</div>
|
||||
</td>
|
||||
</tr>` : ''}`;
|
||||
}).join('')}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
Reference in New Issue
Block a user