This commit is contained in:
Lizandro Guarnizo
2026-05-12 19:41:17 -05:00
parent 55c68b470b
commit 25e3cd6254
9 changed files with 270 additions and 3 deletions
@@ -77,6 +77,9 @@
<button @click="enviarCorreo(d)" title="Enviar correo" class="text-gray-400 hover:text-purple-500">
<svg class="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M21.75 6.75v10.5a2.25 2.25 0 0 1-2.25 2.25h-15a2.25 2.25 0 0 1-2.25-2.25V6.75m19.5 0A2.25 2.25 0 0 0 19.5 4.5h-15a2.25 2.25 0 0 0-2.25 2.25m19.5 0v.243a2.25 2.25 0 0 1-1.07 1.916l-7.5 4.615a2.25 2.25 0 0 1-2.36 0L3.32 8.91a2.25 2.25 0 0 1-1.07-1.916V6.75"/></svg>
</button>
<button @click="openHistorial(d)" title="Ver historial" class="text-gray-400 hover:text-indigo-500">
<svg class="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M12 6v6h4.5m4.5 0a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z"/></svg>
</button>
<button @click="openDelete(d)" title="Eliminar" class="text-gray-400 hover:text-red-500">
<svg class="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="m14.74 9-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 0 1-2.244 2.077H8.084a2.25 2.25 0 0 1-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 0 0-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 0 1 3.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 0 0-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 0 0-7.5 0"/></svg>
</button>
@@ -174,6 +177,69 @@
</div>
</div>
<!-- Modal Historial -->
<div x-show="historialModal" x-cloak class="fixed inset-0 z-50 flex items-end sm:items-center justify-center bg-black/50">
<div class="bg-white rounded-t-2xl sm:rounded-xl shadow-2xl w-full sm:max-w-lg mx-0 sm:mx-4 flex flex-col max-h-[85vh]" @click.stop>
<div class="flex items-center justify-between px-5 py-4 border-b">
<div>
<h2 class="text-base font-semibold" x-text="historialTitulo"></h2>
<p class="text-xs text-gray-400">Línea de tiempo de actividad</p>
</div>
<button @click="historialModal=false" class="text-gray-400 hover:text-gray-600">
<svg class="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M6 18 18 6M6 6l12 12"/></svg>
</button>
</div>
<div class="overflow-y-auto p-5 flex-1">
<!-- Loading -->
<div x-show="historialLoading" class="flex justify-center py-8">
<svg class="animate-spin w-6 h-6 text-indigo-500" fill="none" viewBox="0 0 24 24"><circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"/><path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z"/></svg>
</div>
<!-- Timeline -->
<div x-show="!historialLoading">
<div x-show="historialTimeline.length===0" class="text-center text-gray-400 py-8 text-sm">Sin actividad registrada</div>
<ol class="relative border-l border-gray-200 ml-3">
<template x-for="(ev, i) in historialTimeline" :key="i">
<li class="mb-6 ml-5">
<!-- Dot -->
<span class="absolute -left-3 flex items-center justify-center w-6 h-6 rounded-full ring-4 ring-white"
:class="{
'bg-indigo-100': ev.tipo==='creacion',
'bg-blue-100': ev.tipo==='renovacion',
'bg-purple-100': ev.tipo==='notificacion',
'bg-green-100': ev.tipo==='pago' && ev.estado==='ok',
'bg-red-100': ev.estado==='error'
}">
<!-- document -->
<template x-if="ev.icono==='document'">
<svg class="w-3 h-3 text-indigo-600" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M19.5 14.25v-2.625a3.375 3.375 0 0 0-3.375-3.375h-1.5A1.125 1.125 0 0 1 13.5 7.125v-1.5a3.375 3.375 0 0 0-3.375-3.375H8.25m0 12.75h7.5m-7.5 3H12M10.5 2.25H5.625c-.621 0-1.125.504-1.125 1.125v17.25c0 .621.504 1.125 1.125 1.125h12.75c.621 0 1.125-.504 1.125-1.125V11.25a9 9 0 0 0-9-9Z"/></svg>
</template>
<!-- refresh -->
<template x-if="ev.icono==='refresh'">
<svg class="w-3 h-3 text-blue-600" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M16.023 9.348h4.992v-.001M2.985 19.644v-4.992m0 0h4.992m-4.993 0 3.181 3.183a8.25 8.25 0 0 0 13.803-3.7M4.031 9.865a8.25 8.25 0 0 1 13.803-3.7l3.181 3.182m0-4.991v4.99"/></svg>
</template>
<!-- mail -->
<template x-if="ev.icono==='mail'">
<svg class="w-3 h-3 text-purple-600" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M21.75 6.75v10.5a2.25 2.25 0 0 1-2.25 2.25h-15a2.25 2.25 0 0 1-2.25-2.25V6.75m19.5 0A2.25 2.25 0 0 0 19.5 4.5h-15a2.25 2.25 0 0 0-2.25 2.25m19.5 0v.243a2.25 2.25 0 0 1-1.07 1.916l-7.5 4.615a2.25 2.25 0 0 1-2.36 0L3.32 8.91a2.25 2.25 0 0 1-1.07-1.916V6.75"/></svg>
</template>
<!-- currency -->
<template x-if="ev.icono==='currency'">
<svg class="w-3 h-3 text-green-600" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M2.25 18.75a60.07 60.07 0 0 1 15.797 2.101c.727.198 1.453-.342 1.453-1.096V18.75M3.75 4.5v.75A.75.75 0 0 1 3 6h-.75m0 0v-.375c0-.621.504-1.125 1.125-1.125H20.25M2.25 6v9m18-10.5v.75c0 .414.336.75.75.75h.75m-1.5-1.5h.375c.621 0 1.125.504 1.125 1.125v9.75c0 .621-.504 1.125-1.125 1.125h-.375m1.5-1.5H21a.75.75 0 0 0-.75.75v.75m0 0H3.75m0 0h-.375a1.125 1.125 0 0 1-1.125-1.125V15m1.5 1.5v-.75A.75.75 0 0 0 3 15h-.75M15 10.5a3 3 0 1 1-6 0 3 3 0 0 1 6 0Zm3 0h.008v.008H18V10.5Zm-12 0h.008v.008H6V10.5Z"/></svg>
</template>
</span>
<div class="ml-1">
<p class="text-sm font-medium text-gray-800" x-text="ev.titulo"></p>
<p class="text-xs text-gray-500 mt-0.5" x-text="ev.detalle"></p>
<time class="text-xs text-gray-400" x-text="fmtDatetime(ev.fecha)"></time>
<span x-show="ev.estado==='error'" class="ml-2 text-xs text-red-500 font-medium">Fallido</span>
</div>
</li>
</template>
</ol>
</div>
</div>
</div>
</div>
<!-- Modal Enviar Notificación -->
<div x-show="notifModal" x-cloak class="fixed inset-0 z-50 flex items-center justify-center bg-black/50">
<div class="bg-white rounded-lg shadow-xl w-full max-w-sm mx-4 p-6" @click.stop>
@@ -250,6 +316,7 @@ document.addEventListener('alpine:init', () => {
addModal: false, editModal: false, deleteModal: false,
notifModal: false, notifReglaId: '', notifContratoId: null, notifCliente: '',
bienvenidaModal: false, bienvenidaReglaId: '', bienvenidaContratoId: null,
historialModal: false, historialTitulo: '', historialTimeline: [], historialLoading: false,
clientes: [], servicios: [], reglas: [], reglasBienvenida: [],
selectedId: null,
form: { cliente_id:'', servicio_ids:[], fecha_inicio:'', fecha_vencimiento:'', precio_acordado:0, estado:'activo', auto_renovar:false, notas:'' },
@@ -404,6 +471,23 @@ document.addEventListener('alpine:init', () => {
return m[tipo] || tipo;
},
async openHistorial(d) {
this.historialTitulo = 'Historial — ' + (d.cliente?.nombre || '#'+d.ID);
this.historialTimeline = [];
this.historialLoading = true;
this.historialModal = true;
try {
const res = await axios.get(`/app/api/contratos/${d.ID}/historial`);
this.historialTimeline = res.data.timeline || [];
} catch(e) { this.showToast('Error cargando historial', 'error'); }
this.historialLoading = false;
},
fmtDatetime(raw) {
if (!raw) return '—';
return new Date(raw).toLocaleString('es-ES', { day:'2-digit', month:'2-digit', year:'numeric', hour:'2-digit', minute:'2-digit' });
},
fmtDate(raw) {
if (!raw) return '—';
return new Date(raw).toLocaleDateString('es-ES', { day:'2-digit', month:'2-digit', year:'numeric' });