78 lines
3.2 KiB
HTML
78 lines
3.2 KiB
HTML
{% extends "base.html" %}
|
||||
|
|
{% block title %}Dashboard{% endblock %}
|
|||
|
|
{% block content %}
|
|||
|
|
<div class="section-head">
|
|||
|
|
<div>
|
|||
|
|
<span class="section-tag">// console › overview</span>
|
|||
|
|
<h1>Operations dashboard</h1>
|
|||
|
|
<p class="lead">System status as of <span class="mono">{{ today.isoformat() }}</span>.</p>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="stat-grid">
|
|||
|
|
<div class="stat stat-accent">
|
|||
|
|
<div class="stat-label">Active alerts</div>
|
|||
|
|
<div class="stat-value">{{ active_alerts }}</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="stat">
|
|||
|
|
<div class="stat-label">Users</div>
|
|||
|
|
<div class="stat-value">{{ user_count }}</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="stat stat-warn">
|
|||
|
|
<div class="stat-label">Administrators</div>
|
|||
|
|
<div class="stat-value">{{ admin_count }}</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div style="display:grid; grid-template-columns: 1.2fr 1fr; gap: 20px;">
|
|||
|
|
<section class="panel">
|
|||
|
|
<header class="panel-header">
|
|||
|
|
<h3>Expiring soonest</h3>
|
|||
|
|
<a href="{{ url_for('backoffice.settings') }}" class="muted mono" style="font-size: .82rem;">settings →</a>
|
|||
|
|
</header>
|
|||
|
|
<div class="panel-body">
|
|||
|
|
{% if expiring_soon %}
|
|||
|
|
<table class="table" style="border: none;">
|
|||
|
|
<thead>
|
|||
|
|
<tr><th>Title</th><th>Expires</th><th>Days</th></tr>
|
|||
|
|
</thead>
|
|||
|
|
<tbody>
|
|||
|
|
{% for a in expiring_soon %}
|
|||
|
|
{% set days = (a.expiration_date - today).days %}
|
|||
|
|
<tr>
|
|||
|
|
<td><strong>{{ a.title }}</strong>{% if a.category %} <span class="alert-cat">{{ a.category }}</span>{% endif %}</td>
|
|||
|
|
<td class="mono">{{ a.expiration_date.isoformat() }}</td>
|
|||
|
|
<td class="mono" style="color: {% if days < 7 %}var(--danger){% elif days < 30 %}var(--amber){% else %}var(--accent){% endif %};">{{ days }}d</td>
|
|||
|
|
</tr>
|
|||
|
|
{% endfor %}
|
|||
|
|
</tbody>
|
|||
|
|
</table>
|
|||
|
|
{% else %}
|
|||
|
|
<p class="dim">No upcoming alerts.</p>
|
|||
|
|
{% endif %}
|
|||
|
|
</div>
|
|||
|
|
</section>
|
|||
|
|
|
|||
|
|
<section class="panel">
|
|||
|
|
<header class="panel-header"><h3>Recent activity</h3>
|
|||
|
|
<a href="{{ url_for('backoffice.audit_log') }}" class="muted mono" style="font-size: .82rem;">full log →</a>
|
|||
|
|
</header>
|
|||
|
|
<div class="panel-body">
|
|||
|
|
{% if recent_audit %}
|
|||
|
|
<ul style="list-style: none; padding: 0; margin: 0; display: grid; gap: 8px;">
|
|||
|
|
{% for entry in recent_audit %}
|
|||
|
|
<li class="mono" style="font-size: .82rem; display: grid; grid-template-columns: 1fr auto; gap: 8px; padding: 6px 0; border-bottom: 1px dashed var(--border);">
|
|||
|
|
<span><span style="color: var(--accent);">{{ entry.action }}</span> {% if entry.username %}<span class="dim">· {{ entry.username }}</span>{% endif %}{% if entry.details %}<br><span class="dim">{{ entry.details }}</span>{% endif %}</span>
|
|||
|
|
<span class="dim" style="white-space: nowrap;">{{ entry.timestamp.strftime('%m-%d %H:%M') }}</span>
|
|||
|
|
</li>
|
|||
|
|
{% endfor %}
|
|||
|
|
</ul>
|
|||
|
|
{% else %}
|
|||
|
|
<p class="dim">No activity yet.</p>
|
|||
|
|
{% endif %}
|
|||
|
|
</div>
|
|||
|
|
</section>
|
|||
|
|
</div>
|
|||
|
|
<style>@media (max-width: 800px) { div[style*="grid-template-columns: 1.2fr 1fr"] { grid-template-columns: 1fr !important; } }</style>
|
|||
|
|
{% endblock %}
|