This commit is contained in:
Lizandro Guarnizo
2026-05-06 15:29:14 -05:00
parent ccd37ff459
commit 1a8cc92a22
10 changed files with 908 additions and 0 deletions
+96
View File
@@ -0,0 +1,96 @@
<!-- Vista: Logs de despacho SaaS — historial de notificaciones de pago -->
<div x-data="dispatchLogsApp()" x-init="init()" class="bg-white rounded-lg shadow">
<div class="container mx-auto p-6 w-full">
<div class="flex items-center gap-3 mb-1">
<a href="/app/saas-api" class="text-blue-500 hover:underline text-sm">← Integraciones</a>
</div>
<h1 class="text-2xl font-bold mb-1">Logs de despacho</h1>
<p class="text-sm text-gray-500 mb-4">Historial de notificaciones enviadas a SaaS externos al confirmar pagos.</p>
<!-- Tabla -->
<div class="overflow-x-auto">
<table class="table-auto w-full text-sm">
<thead class="border-b border-gray-200 text-left font-semibold text-gray-600">
<tr>
<th class="py-2 px-3 border-b">Fecha</th>
<th class="py-2 px-3 border-b">Contrato</th>
<th class="py-2 px-3 border-b">Email</th>
<th class="py-2 px-3 border-b">Integración</th>
<th class="py-2 px-3 border-b">Fuente</th>
<th class="py-2 px-3 border-b">HTTP</th>
<th class="py-2 px-3 border-b">Estado</th>
<th class="py-2 px-3 border-b">Respuesta</th>
</tr>
</thead>
<tbody class="text-gray-500">
<template x-if="items.length === 0 && !loading">
<tr><td colspan="8" class="py-4 text-center text-gray-400">Sin registros</td></tr>
</template>
<template x-for="item in items" :key="item.ID">
<tr class="hover:bg-gray-50 border-b border-gray-100">
<td class="py-2 px-3 text-xs whitespace-nowrap" x-text="fmtDate(item.CreatedAt)"></td>
<td class="py-2 px-3 font-mono text-xs" x-text="item.referencia || ('#' + item.contrato_id)"></td>
<td class="py-2 px-3 text-xs" x-text="item.payer_email || '-'"></td>
<td class="py-2 px-3 text-xs" x-text="item.SaasApiConfig ? item.SaasApiConfig.nombre : ('#' + item.saas_api_config_id)"></td>
<td class="py-2 px-3">
<span x-text="item.fuente"
:class="item.fuente === 'dlocal' ? 'bg-purple-100 text-purple-700' : item.fuente === 'bold' ? 'bg-orange-100 text-orange-700' : 'bg-gray-100 text-gray-600'"
class="text-xs px-2 py-0.5 rounded-full capitalize"></span>
</td>
<td class="py-2 px-3 text-xs font-mono" x-text="item.http_status || '-'"></td>
<td class="py-2 px-3">
<span x-text="item.estado"
:class="item.estado === 'success' ? 'bg-green-100 text-green-700' : 'bg-red-100 text-red-700'"
class="text-xs px-2 py-0.5 rounded-full"></span>
</td>
<td class="py-2 px-3 max-w-xs">
<span x-show="!item.error_msg && item.respuesta" class="font-mono text-xs text-gray-600 truncate block" x-text="item.respuesta"></span>
<span x-show="item.error_msg" class="font-mono text-xs text-red-500 truncate block" x-text="item.error_msg"></span>
</td>
</tr>
</template>
</tbody>
</table>
</div>
<!-- Loading -->
<div x-show="loading" class="py-6 text-center text-gray-400 text-sm">Cargando…</div>
<!-- Paginación -->
<div class="flex justify-between items-center mt-4 text-sm text-gray-500">
<span>Total: <b x-text="total"></b></span>
<div class="flex gap-1">
<button @click="load(page-1)" :disabled="page<=1" class="px-3 py-1 border rounded disabled:opacity-40"></button>
<span class="px-3 py-1" x-text="'Pág. ' + page + ' / ' + totalPages"></span>
<button @click="load(page+1)" :disabled="page>=totalPages" class="px-3 py-1 border rounded disabled:opacity-40"></button>
</div>
</div>
</div>
</div>
<script>
function dispatchLogsApp() {
return {
items: [],
total: 0, totalPages: 1, page: 1, limit: 30,
loading: false,
async init() { await this.load(1); },
async load(p) {
if (p < 1 || (this.totalPages > 0 && p > this.totalPages)) return;
this.loading = true;
this.page = p;
const res = await fetch(`/app/loadsaasdispatchlogs?page=${p}`);
const data = await res.json();
this.items = data.items || [];
this.total = data.total;
this.totalPages = data.totalPages;
this.loading = false;
},
fmtDate(s) {
if (!s) return '-';
const d = new Date(s);
return d.toLocaleDateString('es-CO') + ' ' + d.toLocaleTimeString('es-CO', {hour:'2-digit', minute:'2-digit'});
},
};
}
</script>