From f8c7139e6e0c4ce326650cfcac8a0c3a235edb40 Mon Sep 17 00:00:00 2001 From: farentsen Date: Wed, 13 May 2026 16:23:11 +0000 Subject: [PATCH] feat(admin): live-updating leads view with auto-refresh, sort, filter, +N badge --- app/templates/admin_leads.html | 268 ++++++++++++++++++++++++++------- static/css/styles.css | 55 ++++++- 2 files changed, 268 insertions(+), 55 deletions(-) diff --git a/app/templates/admin_leads.html b/app/templates/admin_leads.html index 9424464..10c459c 100644 --- a/app/templates/admin_leads.html +++ b/app/templates/admin_leads.html @@ -5,87 +5,247 @@
-

ADMIN

+

ADMIN · EN VIVO

Leads registrados

-

{{ total }} registros · {{ total_downloads }} descargas totales

+

+ registros · + descargas totales +

+
+ + en vivo · 10s +
+ + Exportar CSV - Ver stats JSON
- + +
- - - - - - - + + + + + + + - - {% for l in leads %} - - - - - - - - - - - - {% endfor %} - {% if not leads %} - - {% endif %} + +
#NombreEmailEmpresaReg.CreadoÚltimo#NombreEmailEmpresaReg.CreadoÚltimo IP Consent
{{ l.id }}{{ l.nombre }}{{ l.email }}{{ l.empresa }}{{ l.times_registered }}{{ l.created_at }}{{ l.last_seen }}{{ l.ip or '—' }}{% if l.consent %}{% else %}×{% endif %}
No hay leads registrados todavía.
Cargando…
- {% if top_empresas %} -
-

Top empresas

-
    - {% for e in top_empresas %} -
  • {{ e.empresa }}{{ e.count }}
  • - {% endfor %} -
+
+
+

Top empresas

+
    +
  • Cargando…
  • +
+
+
+

Descargas por archivo

+
    +
  • Cargando…
  • +
+
- {% endif %} - - {% if downloads_por_archivo %} -
-

Descargas por archivo

-
    - {% for fname, count in downloads_por_archivo.items() %} -
  • {{ fname }}{{ count }}
  • - {% endfor %} -
-
- {% endif %}
{% endblock %} diff --git a/static/css/styles.css b/static/css/styles.css index 9915dda..130ee4f 100644 --- a/static/css/styles.css +++ b/static/css/styles.css @@ -321,7 +321,60 @@ body { .pill-ok { background: rgba(14, 116, 144, 0.12); color: var(--teal); } .pill-no { background: rgba(180, 83, 9, 0.12); color: var(--accent); } -.admin-stats { margin-top: 40px; } +.live-status { + display: inline-flex; align-items: center; gap: 8px; + padding: 8px 14px; background: var(--slate-50); + border: 1px solid var(--slate-200); border-radius: 6px; +} +.dot { + width: 8px; height: 8px; border-radius: 50%; + background: var(--slate-400); + box-shadow: 0 0 0 0 rgba(14, 116, 144, 0.4); +} +.dot-live { + background: #10B981; + animation: pulse 2s infinite; +} +.dot-error { background: var(--accent); animation: none; } +@keyframes pulse { + 0% { box-shadow: 0 0 0 0 rgba(16, 185, 129, 0.45); } + 70% { box-shadow: 0 0 0 8px rgba(16, 185, 129, 0); } + 100% { box-shadow: 0 0 0 0 rgba(16, 185, 129, 0); } +} +.live-label { + font-size: 12px; color: var(--slate-700); + font-weight: 600; letter-spacing: 0.02em; +} +.live-label.flash { + color: #047857; font-weight: 700; + animation: flash-bg 0.6s ease; +} +@keyframes flash-bg { + 0% { transform: scale(1); } + 50% { transform: scale(1.08); } + 100% { transform: scale(1); } +} + +.btn-icon { + padding: 8px 12px !important; font-size: 16px !important; + line-height: 1; min-width: 38px; text-align: center; +} + +.last-updated { + margin-left: 16px; font-size: 12px; color: var(--slate-600); + font-style: italic; +} + +th.sortable { cursor: pointer; user-select: none; transition: background 0.15s ease; } +th.sortable:hover { background: var(--slate-100); } +th.sortable.active { color: var(--navy); } +.sort-ind { font-weight: 700; } + +.admin-stats-grid { + display: grid; grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); + gap: 32px; margin-top: 40px; +} +.admin-stats { margin-top: 0; } .admin-h2 { color: var(--navy); font-size: 18px; font-weight: 600; margin: 0 0 12px; letter-spacing: -0.005em;