Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>System Logs — God Mode</title> | |
| <link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}"> | |
| <link rel="preconnect" href="https://fonts.googleapis.com"> | |
| <link href="https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;500;600;700;800&display=swap" | |
| rel="stylesheet"> | |
| <script src="https://unpkg.com/@phosphor-icons/web"></script> | |
| <script>if (localStorage.getItem('proofly-theme') === 'dark') document.documentElement.setAttribute('data-theme', 'dark');</script> | |
| <style> | |
| .logs-wrap { | |
| padding: 2rem 2.5rem; | |
| max-width: 1400px; | |
| margin: 0 auto; | |
| display: flex; | |
| flex-direction: column; | |
| height: calc(100vh - 80px); | |
| } | |
| .logs-header { | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| margin-bottom: 1.25rem; | |
| gap: 1rem; | |
| flex-shrink: 0; | |
| } | |
| .logs-title { | |
| font-size: 1.4rem; | |
| font-weight: 800; | |
| color: var(--text-main); | |
| } | |
| .filter-bar { | |
| display: flex; | |
| gap: 0.75rem; | |
| flex-wrap: wrap; | |
| } | |
| .filter-input { | |
| background: var(--bg-input); | |
| border: 1px solid var(--border-color); | |
| border-radius: var(--radius-sm); | |
| padding: 0.5rem 0.9rem; | |
| font-size: 0.875rem; | |
| color: var(--text-main); | |
| font-family: inherit; | |
| width: 200px; | |
| transition: border-color 0.2s; | |
| } | |
| .filter-input:focus { | |
| outline: none; | |
| border-color: var(--primary); | |
| } | |
| .filter-select { | |
| background: var(--bg-input); | |
| border: 1px solid var(--border-color); | |
| border-radius: var(--radius-sm); | |
| padding: 0.5rem 0.9rem; | |
| font-size: 0.875rem; | |
| color: var(--text-main); | |
| font-family: inherit; | |
| cursor: pointer; | |
| } | |
| .table-wrap { | |
| flex: 1; | |
| overflow-y: auto; | |
| border-radius: var(--radius-md); | |
| border: 1px solid var(--border-color); | |
| background: var(--bg-card); | |
| } | |
| .table-wrap::-webkit-scrollbar { | |
| width: 7px; | |
| } | |
| .table-wrap::-webkit-scrollbar-track { | |
| background: transparent; | |
| } | |
| .table-wrap::-webkit-scrollbar-thumb { | |
| background: var(--border-color); | |
| border-radius: 4px; | |
| } | |
| .logs-table { | |
| width: 100%; | |
| border-collapse: collapse; | |
| font-size: 0.875rem; | |
| } | |
| .logs-table thead th { | |
| position: sticky; | |
| top: 0; | |
| background: var(--bg-card); | |
| backdrop-filter: blur(8px); | |
| text-align: left; | |
| padding: 0.85rem 1rem; | |
| border-bottom: 2px solid var(--border-color); | |
| color: var(--text-muted); | |
| font-size: 0.72rem; | |
| text-transform: uppercase; | |
| letter-spacing: 0.06em; | |
| z-index: 10; | |
| } | |
| .logs-table td { | |
| padding: 0.85rem 1rem; | |
| border-bottom: 1px solid var(--border-color); | |
| color: var(--text-main); | |
| } | |
| .logs-table tr:last-child td { | |
| border-bottom: none; | |
| } | |
| .logs-table tr:hover td { | |
| background: var(--bg-input); | |
| } | |
| .claim-cell { | |
| max-width: 380px; | |
| overflow: hidden; | |
| text-overflow: ellipsis; | |
| white-space: nowrap; | |
| } | |
| .badge { | |
| padding: 0.2rem 0.6rem; | |
| border-radius: 20px; | |
| font-size: 0.72rem; | |
| font-weight: 700; | |
| } | |
| .badge-true { | |
| background: rgba(16, 185, 129, 0.1); | |
| color: #10b981; | |
| } | |
| .badge-false { | |
| background: rgba(239, 68, 68, 0.1); | |
| color: #ef4444; | |
| } | |
| .badge-uncertain { | |
| background: rgba(245, 158, 11, 0.1); | |
| color: #f59e0b; | |
| } | |
| .row-count { | |
| font-size: 0.8rem; | |
| color: var(--text-muted); | |
| align-self: center; | |
| } | |
| .no-results { | |
| text-align: center; | |
| padding: 4rem; | |
| color: var(--text-muted); | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="app-container"> | |
| <!-- Sidebar --> | |
| <aside class="sidebar"> | |
| <div class="sidebar-top"> | |
| <a href="/" class="nav-btn" title="Home" style="text-decoration:none;"><i class="ph ph-house"></i></a> | |
| <div class="spacer"></div> | |
| <a href="/admin" class="nav-btn" title="Dashboard" style="text-decoration:none;"><i | |
| class="ph ph-chart-pie-slice"></i></a> | |
| <a href="/admin/users" class="nav-btn" title="Users" style="text-decoration:none;"><i | |
| class="ph ph-users"></i></a> | |
| <a href="/admin/logs" class="icon-btn active-icon" title="Logs" style="text-decoration:none;"><i | |
| class="ph ph-rows"></i></a> | |
| </div> | |
| <div class="sidebar-bottom"> | |
| <button class="theme-toggle-btn" title="Toggle theme" onclick="toggleTheme()"> | |
| <i class="ph ph-moon icon-moon"></i> | |
| <i class="ph ph-sun icon-sun"></i> | |
| </button> | |
| </div> | |
| </aside> | |
| <!-- Main --> | |
| <main class="main-content"> | |
| <header class="top-header"> | |
| <div class="header-center"><span class="daily-text">Global Fact Check Logs</span></div> | |
| </header> | |
| <div class="logs-wrap"> | |
| <div class="logs-header"> | |
| <div style="display:flex; align-items:center; gap:1rem;"> | |
| <span class="logs-title">System Logs</span> | |
| <span class="row-count" id="rowCount">{{ history|length }} records</span> | |
| </div> | |
| <div class="filter-bar"> | |
| <input type="text" class="filter-input" id="searchInput" placeholder="Search claim or user…" | |
| oninput="filterTable()"> | |
| <select class="filter-select" id="verdictFilter" onchange="filterTable()"> | |
| <option value="">All Verdicts</option> | |
| <option value="TRUE">TRUE</option> | |
| <option value="FALSE">FALSE</option> | |
| <option value="UNCERTAIN">UNCERTAIN</option> | |
| </select> | |
| </div> | |
| </div> | |
| <div class="table-wrap"> | |
| <table class="logs-table" id="logsTable"> | |
| <thead> | |
| <tr> | |
| <th>#</th> | |
| <th>User</th> | |
| <th>Claim</th> | |
| <th>Verdict</th> | |
| <th>Confidence</th> | |
| <th>Evidence</th> | |
| <th>Date</th> | |
| </tr> | |
| </thead> | |
| <tbody id="logsBody"> | |
| {% for item in history %} | |
| <tr data-user="{{ item.username|lower }}" data-claim="{{ item.claim|lower }}" | |
| data-verdict="{{ item.verdict }}"> | |
| <td style="color: var(--text-muted);">{{ loop.index }}</td> | |
| <td><strong>{{ item.username }}</strong></td> | |
| <td class="claim-cell" title="{{ item.claim }}">{{ item.claim }}</td> | |
| <td><span class="badge badge-{{ item.verdict|lower }}">{{ item.verdict }}</span></td> | |
| <td>{{ (item.confidence * 100)|round(1) }}%</td> | |
| <td style="color: var(--text-muted);">{{ item.evidence_count }} src</td> | |
| <td style="color: var(--text-muted); font-size: 0.82rem; white-space: nowrap;">{{ | |
| item.created_at.strftime('%Y-%m-%d %H:%M') }}</td> | |
| </tr> | |
| {% endfor %} | |
| </tbody> | |
| </table> | |
| <div class="no-results" id="noResults" style="display:none;"> | |
| <i class="ph ph-magnifying-glass" | |
| style="font-size:2.5rem;opacity:0.3;display:block;margin-bottom:0.75rem;"></i> | |
| No records match your filter. | |
| </div> | |
| </div> | |
| </div> | |
| </main> | |
| </div> | |
| <script> | |
| function toggleTheme() { | |
| const isDark = document.documentElement.getAttribute('data-theme') === 'dark'; | |
| if (isDark) { document.documentElement.removeAttribute('data-theme'); localStorage.setItem('proofly-theme', 'light'); } | |
| else { document.documentElement.setAttribute('data-theme', 'dark'); localStorage.setItem('proofly-theme', 'dark'); } | |
| } | |
| function filterTable() { | |
| const search = document.getElementById('searchInput').value.toLowerCase(); | |
| const verdict = document.getElementById('verdictFilter').value; | |
| const rows = document.querySelectorAll('#logsBody tr'); | |
| let visible = 0; | |
| rows.forEach(row => { | |
| const matchSearch = !search || row.dataset.user.includes(search) || row.dataset.claim.includes(search); | |
| const matchVerdict = !verdict || row.dataset.verdict === verdict; | |
| const show = matchSearch && matchVerdict; | |
| row.style.display = show ? '' : 'none'; | |
| if (show) visible++; | |
| }); | |
| document.getElementById('rowCount').textContent = `${visible} records`; | |
| document.getElementById('noResults').style.display = visible === 0 ? 'block' : 'none'; | |
| } | |
| </script> | |
| </body> | |
| </html> |