Download templates/admin.html from Binta26/WEB_DEV: direct link, hf CLI and curl.
- Browser
- Download file 6.6 kB
-
https://huggingface.co/spaces/Binta26/WEB_DEV/resolve/main/templates/admin.html
- Command line
-
hf download hf://spaces/Binta26/WEB_DEV/templates/admin.html
-
curl -L -o admin.html https://huggingface.co/spaces/Binta26/WEB_DEV/resolve/main/templates/admin.html
6.6 kB
| {% extends "base.html" %} | |
| {% block title %}Admin — IrisAI{% endblock %} | |
| {% block head %} | |
| <style> | |
| /* ── Stats grid ── */ | |
| .stats-grid { | |
| display: grid; | |
| grid-template-columns: repeat(4, 1fr); | |
| gap: 1rem; | |
| margin-bottom: 2rem; | |
| } | |
| @media (max-width: 700px) { .stats-grid { grid-template-columns: 1fr 1fr; } } | |
| .stat-card { | |
| background: var(--surface); | |
| border: 1px solid var(--border); | |
| border-radius: var(--radius-lg); | |
| padding: 1.25rem 1.5rem; | |
| text-align: center; | |
| } | |
| .stat-card .val { | |
| font-family: 'DM Serif Display', serif; | |
| font-size: 2.8rem; | |
| background: linear-gradient(135deg, var(--accent), var(--accent2)); | |
| -webkit-background-clip: text; | |
| -webkit-text-fill-color: transparent; | |
| } | |
| .stat-card .lbl { | |
| font-size: .80rem; | |
| text-transform: uppercase; | |
| letter-spacing: .05em; | |
| color: var(--muted); | |
| margin-top: .2rem; | |
| } | |
| .stat-card.active-card { border-color: #4ade80; } | |
| .stat-card.active-card .val { background: linear-gradient(135deg, #4ade80, #22d3ee); -webkit-background-clip: text; -webkit-text-fill-color: transparent; } | |
| /* ── Section title ── */ | |
| .section-title { | |
| font-size: .80rem; | |
| text-transform: uppercase; | |
| letter-spacing: .08em; | |
| color: var(--muted); | |
| margin-bottom: .75rem; | |
| font-weight: 700; | |
| } | |
| /* ── Status dot ── */ | |
| .dot { | |
| display: inline-block; | |
| width: 8px; height: 8px; | |
| border-radius: 50%; | |
| margin-right: 6px; | |
| vertical-align: middle; | |
| } | |
| .dot-green { background: #4ade80; box-shadow: 0 0 6px #4ade8088; animation: pulse 1.5s infinite; } | |
| .dot-gray { background: var(--muted); } | |
| @keyframes pulse { | |
| 0%, 100% { opacity: 1; } | |
| 50% { opacity: .4; } | |
| } | |
| /* ── Refresh badge ── */ | |
| .refresh-badge { | |
| font-family: 'DM Mono', monospace; | |
| font-size: .80rem; | |
| background: var(--surface2); | |
| border: 1px solid var(--border); | |
| color: var(--muted); | |
| padding: .2rem .7rem; | |
| border-radius: 20px; | |
| } | |
| #countdown { color: var(--accent); } | |
| /* ── Table overrides ── */ | |
| .admin-table th { white-space: nowrap; } | |
| .admin-table td { font-size: .82rem; white-space: nowrap; } | |
| .overflow-x { overflow-x: auto; } | |
| input[type="email"] { | |
| width: 100%; padding: .75rem 1rem; | |
| background: var(--surface2); border: 1px solid var(--border); | |
| border-radius: var(--radius); color: var(--text); | |
| font-family: inherit; font-size: .95rem; outline: none; | |
| transition: border-color .2s, box-shadow .2s; | |
| } | |
| input[type="email"]:focus { | |
| border-color: var(--accent); | |
| box-shadow: 0 0 0 3px rgba(124,111,255,.15); | |
| } | |
| </style> | |
| {% endblock %} | |
| {% block body %} | |
| <div class="page"> | |
| <div class="container" style="max-width:1100px"> | |
| <!-- Header --> | |
| <div class="page-header" style="display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:1rem"> | |
| <div> | |
| <h1>🛠 Admin Dashboard</h1> | |
| <p>Real-time database</p> | |
| </div> | |
| <span class="refresh-badge">Refresh in <span id="countdown">5</span>s</span> | |
| </div> | |
| <!-- Stats --> | |
| <div class="stats-grid"> | |
| <div class="stat-card"> | |
| <div class="val">{{ stats.total_users }}</div> | |
| <div class="lbl">Users</div> | |
| </div> | |
| <div class="stat-card active-card"> | |
| <div class="val">{{ stats.active_now }}</div> | |
| <div class="lbl">Currently online</div> | |
| </div> | |
| <div class="stat-card"> | |
| <div class="val">{{ stats.total_sessions }}</div> | |
| <div class="lbl">Total sessions</div> | |
| </div> | |
| <div class="stat-card"> | |
| <div class="val">{{ stats.total_preds }}</div> | |
| <div class="lbl">Prédictions</div> | |
| </div> | |
| </div> | |
| <!-- Table users --> | |
| <div class="card" style="margin-bottom:1.5rem"> | |
| <p class="section-title">👤 Users Table</p> | |
| <div class="overflow-x"> | |
| <table class="admin-table"> | |
| <thead> | |
| <tr> | |
| <th>ID</th> | |
| <th>Username</th> | |
| <th>Email</th> | |
| <th>Create at </th> | |
| </tr> | |
| </thead> | |
| <tbody> | |
| {% for u in users %} | |
| <tr> | |
| <td class="mono muted">{{ u.id }}</td> | |
| <td><strong>{{ u.username }}</strong></td> | |
| <td class="muted">{{ u.email }}</td> | |
| <td class="muted">{{ u.created[:19].replace('T', ' ') }}</td> | |
| </tr> | |
| {% else %} | |
| <tr><td colspan="4" class="muted" style="text-align:center;padding:1.5rem">No users</td></tr> | |
| {% endfor %} | |
| </tbody> | |
| </table> | |
| </div> | |
| </div> | |
| <!-- Table sessions --> | |
| <div class="card"> | |
| <p class="section-title">🔐 Table sessions — logins/logouts (last 50)</p> | |
| <div class="overflow-x"> | |
| <table class="admin-table"> | |
| <thead> | |
| <tr> | |
| <th>ID</th> | |
| <th>User</th> | |
| <th>Email</th> | |
| <th>Last logged in on</th> | |
| <th>Logged out on</th> | |
| <th>Status</th> | |
| </tr> | |
| </thead> | |
| <tbody> | |
| {% for s in sessions_log %} | |
| <tr> | |
| <td class="mono muted">{{ s.id }}</td> | |
| <td><strong>{{ s.username }}</strong></td> | |
| <td class="muted">{{ s.email }}</td> | |
| <td class="muted">{{ s.login_at[:19].replace('T', ' ') }}</td> | |
| <td class="muted"> | |
| {% if s.logout_at %} | |
| {{ s.logout_at[:19].replace('T', ' ') }} | |
| {% else %} | |
| <span class="muted">—</span> | |
| {% endif %} | |
| </td> | |
| <td> | |
| {% if s.logout_at %} | |
| <span class="dot dot-gray"></span><span class="muted" style="font-size:.8rem">Disconnect</span> | |
| {% else %} | |
| <span class="dot dot-green"></span><span style="color:#4ade80;font-size:.8rem">On line</span> | |
| {% endif %} | |
| </td> | |
| </tr> | |
| {% else %} | |
| <tr><td colspan="6" class="muted" style="text-align:center;padding:1.5rem">No sessions saved</td></tr> | |
| {% endfor %} | |
| </tbody> | |
| </table> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <script> | |
| // Rafraîchissement automatique toutes les 5 secondes | |
| let count = 5; | |
| const el = document.getElementById('countdown'); | |
| setInterval(() => { | |
| count--; | |
| el.textContent = count; | |
| if (count <= 0) { | |
| window.location.reload(); | |
| } | |
| }, 1000); | |
| </script> | |
| {% endblock %} | |