Files
rips_manager/app/templates/actividad.html
T
Lizandro GuarnizoandClaude Sonnet 4.6 c20e1a7476 feat: log de actividad por usuario
- Tabla activity_log en SQLite (migración automática)
- Registra: login, login_fallido, rda_enviado, rda_masivo, rda_directo,
  automation_run, contrato_creado, contrato_editado, contrato_toggle
- Endpoint GET /logs/actividad con filtros por usuario, acción y fecha
- UI /logs/actividad con badges de color por tipo de acción
- Enlace "Actividad" en navegación lateral

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-10 17:11:33 -05:00

146 lines
8.1 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{% extends "base.html" %}
{% block title %}Actividad{% endblock %}
{% block header %}Registro de Actividad{% endblock %}
{% block content %}
<!-- Tabs -->
<div class="flex gap-2 mb-5">
<a href="/logs" class="px-4 py-2 rounded-lg text-sm font-medium bg-white border border-gray-200 text-gray-600 hover:bg-gray-50">
<i class="fas fa-paper-plane mr-1"></i> Envíos TNS
</a>
<span class="px-4 py-2 rounded-lg text-sm font-medium bg-blue-600 text-white">
<i class="fas fa-user-clock mr-1"></i> Actividad usuarios
</span>
</div>
<!-- Filtros -->
<div class="bg-white rounded-xl shadow-sm border border-gray-200 mb-5">
<form method="get" action="/logs/actividad" class="p-4">
<input type="hidden" name="offset" value="0">
<div class="flex flex-wrap items-end gap-3">
<div>
<label class="block text-xs font-medium text-gray-500 mb-1">Usuario</label>
<select name="username" class="px-3 py-1.5 border border-gray-300 rounded-lg text-sm min-w-[130px]">
<option value="">Todos</option>
{% for u in usuarios %}
<option value="{{ u }}" {{ 'selected' if filtro_username == u }}>{{ u }}</option>
{% endfor %}
</select>
</div>
<div>
<label class="block text-xs font-medium text-gray-500 mb-1">Acción</label>
<select name="accion" class="px-3 py-1.5 border border-gray-300 rounded-lg text-sm min-w-[150px]">
<option value="">Todas</option>
{% for a in acciones %}
<option value="{{ a }}" {{ 'selected' if filtro_accion == a }}>{{ a }}</option>
{% endfor %}
</select>
</div>
<div>
<label class="block text-xs font-medium text-gray-500 mb-1">Desde</label>
<input type="date" name="fecha_desde" value="{{ filtro_fecha_desde }}"
class="px-3 py-1.5 border border-gray-300 rounded-lg text-sm">
</div>
<div>
<label class="block text-xs font-medium text-gray-500 mb-1">Hasta</label>
<input type="date" name="fecha_hasta" value="{{ filtro_fecha_hasta }}"
class="px-3 py-1.5 border border-gray-300 rounded-lg text-sm">
</div>
<div class="flex gap-2">
<button type="submit" class="px-4 py-1.5 bg-blue-600 text-white rounded-lg text-sm hover:bg-blue-700">
<i class="fas fa-search mr-1"></i> Filtrar
</button>
<a href="/logs/actividad" class="px-3 py-1.5 bg-gray-100 text-gray-600 rounded-lg text-sm hover:bg-gray-200" title="Limpiar">
<i class="fas fa-times"></i>
</a>
</div>
</div>
</form>
</div>
<!-- Tabla -->
<div class="bg-white rounded-xl shadow-sm border border-gray-200">
<div class="px-6 py-4 border-b border-gray-200 flex items-center justify-between">
<h3 class="font-semibold text-gray-800"><i class="fas fa-list mr-2 text-gray-400"></i>Eventos</h3>
<span class="text-xs text-gray-400">{{ total }} registro(s)</span>
</div>
<div class="overflow-x-auto">
<table class="w-full text-sm">
<thead class="bg-gray-50 border-b border-gray-200">
<tr>
<th class="px-4 py-3 text-left text-xs font-semibold text-gray-500 uppercase">Fecha/Hora</th>
<th class="px-4 py-3 text-left text-xs font-semibold text-gray-500 uppercase">Usuario</th>
<th class="px-4 py-3 text-left text-xs font-semibold text-gray-500 uppercase">Acción</th>
<th class="px-4 py-3 text-left text-xs font-semibold text-gray-500 uppercase">Detalle</th>
<th class="px-4 py-3 text-left text-xs font-semibold text-gray-500 uppercase">IP</th>
</tr>
</thead>
<tbody class="divide-y divide-gray-100">
{% for r in rows %}
<tr class="hover:bg-gray-50">
<td class="px-4 py-2.5 text-xs text-gray-500 font-mono whitespace-nowrap">
{{ r.created_at[:16].replace('T',' ') }}
</td>
<td class="px-4 py-2.5">
<span class="font-medium text-gray-700">{{ r.username }}</span>
</td>
<td class="px-4 py-2.5">
{% set a = r.accion %}
{% if a == 'login' %}
<span class="px-2 py-0.5 bg-green-100 text-green-700 rounded-full text-xs font-medium">Login</span>
{% elif a == 'login_fallido' %}
<span class="px-2 py-0.5 bg-red-100 text-red-700 rounded-full text-xs font-medium">Login fallido</span>
{% elif a == 'rda_enviado' %}
<span class="px-2 py-0.5 bg-purple-100 text-purple-700 rounded-full text-xs font-medium">RDA enviado</span>
{% elif a == 'rda_masivo' %}
<span class="px-2 py-0.5 bg-indigo-100 text-indigo-700 rounded-full text-xs font-medium">RDA masivo</span>
{% elif a == 'rda_directo' %}
<span class="px-2 py-0.5 bg-blue-100 text-blue-700 rounded-full text-xs font-medium">RDA directo</span>
{% elif a == 'automation_run' %}
<span class="px-2 py-0.5 bg-orange-100 text-orange-700 rounded-full text-xs font-medium">Automatización</span>
{% elif a == 'contrato_creado' %}
<span class="px-2 py-0.5 bg-teal-100 text-teal-700 rounded-full text-xs font-medium">Contrato creado</span>
{% elif a == 'contrato_editado' %}
<span class="px-2 py-0.5 bg-yellow-100 text-yellow-700 rounded-full text-xs font-medium">Contrato editado</span>
{% elif a == 'contrato_toggle' %}
<span class="px-2 py-0.5 bg-gray-100 text-gray-700 rounded-full text-xs font-medium">Contrato toggle</span>
{% else %}
<span class="px-2 py-0.5 bg-gray-100 text-gray-600 rounded-full text-xs font-medium">{{ a }}</span>
{% endif %}
</td>
<td class="px-4 py-2.5 text-xs text-gray-600 max-w-xs truncate" title="{{ r.detalle }}">
{{ r.detalle }}
</td>
<td class="px-4 py-2.5 text-xs text-gray-400 font-mono">{{ r.ip }}</td>
</tr>
{% else %}
<tr>
<td colspan="5" class="px-4 py-10 text-center text-gray-400">
No hay eventos registrados aún
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% if has_more or offset > 0 %}
<div class="px-6 py-3 border-t border-gray-100 flex justify-between items-center">
{% if offset > 0 %}
<a href="?username={{ filtro_username }}&accion={{ filtro_accion }}&fecha_desde={{ filtro_fecha_desde }}&fecha_hasta={{ filtro_fecha_hasta }}&offset={{ [offset - page_size, 0]|max }}"
class="px-3 py-1.5 bg-gray-100 text-gray-700 rounded-lg text-xs hover:bg-gray-200">
<i class="fas fa-chevron-left mr-1"></i> Anterior
</a>
{% else %}<span></span>{% endif %}
<span class="text-xs text-gray-400">{{ offset + 1 }}{{ [offset + page_size, total]|min }} de {{ total }}</span>
{% if has_more %}
<a href="?username={{ filtro_username }}&accion={{ filtro_accion }}&fecha_desde={{ filtro_fecha_desde }}&fecha_hasta={{ filtro_fecha_hasta }}&offset={{ offset + page_size }}"
class="px-3 py-1.5 bg-gray-100 text-gray-700 rounded-lg text-xs hover:bg-gray-200">
Siguiente <i class="fas fa-chevron-right ml-1"></i>
</a>
{% else %}<span></span>{% endif %}
</div>
{% endif %}
</div>
{% endblock %}