Dashboard redesign: bc-stat-grid with colored values

- Replace Bootstrap display-6 stats with bc-stat-grid layout:
  Inbox card spans 2 columns; status values colored by type
  (success=green, warning=yellow, failed=red, override=blue, muted=grey)
- Fix extra </div> that caused large empty space below stats
- Compact Legend to horizontal flex-wrap row
- Move System status card above description text
- Shorten description to 3 paragraphs
- Add bc-stat-grid / bc-stat-card CSS to layout.css

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Ivo Oskamp 2026-03-19 15:48:11 +01:00
parent e39e5359a6
commit 96e4e8c143
2 changed files with 120 additions and 111 deletions

View File

@ -486,3 +486,57 @@ main.content-container,
main.dashboard-container { main.dashboard-container {
/* Replaced by .bc-content — these are no-ops now but kept for safety */ /* Replaced by .bc-content — these are no-ops now but kept for safety */
} }
/* ============================================================
DASHBOARD STAT CARDS
============================================================ */
.bc-stat-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
gap: 12px;
}
.bc-stat-card {
background: var(--bs-card-bg);
border: 1px solid var(--bs-border-color);
border-radius: var(--bc-radius);
padding: 14px 16px;
}
.bc-stat-card.bc-stat-large {
grid-column: span 2;
}
.bc-stat-label {
display: flex;
align-items: center;
gap: 5px;
font-size: 11.5px;
font-weight: 500;
opacity: .55;
margin-bottom: 5px;
}
.bc-stat-value {
font-size: 30px;
font-weight: 600;
letter-spacing: -.03em;
line-height: 1;
}
.bc-stat-sub {
font-size: 11px;
opacity: .4;
margin-top: 4px;
}
.bc-stat-success { color: var(--bs-success); }
.bc-stat-warning { color: var(--bs-warning); }
.bc-stat-failed { color: var(--bs-danger); }
.bc-stat-override{ color: var(--bs-primary); }
.bc-stat-muted { color: var(--bs-secondary); }
@media (max-width: 767px) {
.bc-stat-card.bc-stat-large { grid-column: span 1; }
.bc-stat-grid { grid-template-columns: repeat(2, 1fr); }
}

View File

@ -2,95 +2,70 @@
{% block content %} {% block content %}
<h2 class="mb-4">Dashboard</h2> <h2 class="mb-4">Dashboard</h2>
<div class="row g-3 mb-4"> <div class="bc-stat-grid mb-4">
<div class="col-12 col-md-3"> <div class="bc-stat-card bc-stat-large">
<div class="card h-100"> <div class="bc-stat-label">
<div class="card-body"> <svg width="13" height="13" viewBox="0 0 15 15" fill="none"><path d="M1 9.5l2.5-5h8L14 9.5V13a1 1 0 01-1 1H2a1 1 0 01-1-1V9.5z" stroke="currentColor" stroke-width="1.3"/><path d="M1 9.5h3.5a2 2 0 004 0H14" stroke="currentColor" stroke-width="1.3"/></svg>
<div class="text-muted">Inbox</div> Inbox
<div class="display-6 mb-0">{{ inbox_count }}</div> </div>
<div class="text-muted small mt-2">Open items</div> <div class="bc-stat-value">{{ inbox_count }}</div>
<div class="bc-stat-sub">Open items</div>
</div>
<div class="bc-stat-card">
<div class="bc-stat-label"><span class="status-dot dot-success me-1" aria-hidden="true"></span>Success</div>
<div class="bc-stat-value bc-stat-success">{{ jobs_success_count }}</div>
</div>
<div class="bc-stat-card">
<div class="bc-stat-label"><span class="status-dot dot-override me-1" aria-hidden="true"></span>Success (override)</div>
<div class="bc-stat-value bc-stat-override">{{ jobs_success_override_count }}</div>
</div>
<div class="bc-stat-card">
<div class="bc-stat-label"><span class="status-dot dot-expected me-1" aria-hidden="true"></span>Expected</div>
<div class="bc-stat-value bc-stat-muted">{{ jobs_expected_count }}</div>
</div>
<div class="bc-stat-card">
<div class="bc-stat-label"><span class="status-dot dot-warning me-1" aria-hidden="true"></span>Warning</div>
<div class="bc-stat-value bc-stat-warning">{{ jobs_warning_count }}</div>
</div>
<div class="bc-stat-card">
<div class="bc-stat-label"><span class="status-dot dot-failed me-1" aria-hidden="true"></span>Failed</div>
<div class="bc-stat-value bc-stat-failed">{{ jobs_error_count }}</div>
</div>
<div class="bc-stat-card">
<div class="bc-stat-label"><span class="status-dot dot-missed me-1" aria-hidden="true"></span>Missed</div>
<div class="bc-stat-value bc-stat-muted">{{ jobs_missed_count }}</div>
</div>
</div>
<div class="card mb-3">
<div class="card-header py-2"><span class="fw-semibold" style="font-size:13px;">Legend</span></div>
<div class="card-body py-2">
<div class="d-flex flex-wrap gap-x-4 gap-y-1" style="column-gap:2rem;row-gap:.35rem;">
<div class="small"><span class="status-dot dot-success me-2" aria-hidden="true"></span><strong>Success</strong> — job run completed successfully</div>
<div class="small"><span class="status-dot dot-failed me-2" aria-hidden="true"></span><strong>Failed</strong> — job run failed, action required</div>
<div class="small"><span class="status-dot dot-warning me-2" aria-hidden="true"></span><strong>Warning</strong> — job run completed with a warning</div>
<div class="small"><span class="status-dot dot-missed me-2" aria-hidden="true"></span><strong>Missed</strong> — job run expected but did not execute</div>
<div class="small"><span class="status-dot dot-expected me-2" aria-hidden="true"></span><strong>Expected</strong> — job run not yet due</div>
<div class="small"><span class="status-dot dot-override me-2" aria-hidden="true"></span><strong>Success (override)</strong> — marked as successful via override</div>
</div> </div>
</div> </div>
</div> </div>
<div class="col-12 col-md-9">
<div class="row g-3">
<div class="col-6 col-lg-2">
<div class="card h-100">
<div class="card-body">
<div class="text-muted"><span class="status-dot dot-success me-2" aria-hidden="true"></span>Success</div>
<div class="display-6 mb-0">{{ jobs_success_count }}</div>
</div>
</div>
</div>
<div class="col-6 col-lg-2">
<div class="card h-100">
<div class="card-body">
<div class="text-muted"><span class="status-dot dot-override me-2" aria-hidden="true"></span><span class="text-nowrap">Success (override)</span></div>
<div class="display-6 mb-0">{{ jobs_success_override_count }}</div>
</div>
</div>
</div>
<div class="col-6 col-lg-2">
<div class="card h-100">
<div class="card-body">
<div class="text-muted"><span class="status-dot dot-expected me-2" aria-hidden="true"></span>Expected</div>
<div class="display-6 mb-0">{{ jobs_expected_count }}</div>
</div>
</div>
</div>
<div class="col-6 col-lg-2">
<div class="card h-100">
<div class="card-body">
<div class="text-muted"><span class="status-dot dot-warning me-2" aria-hidden="true"></span>Warning</div>
<div class="display-6 mb-0">{{ jobs_warning_count }}</div>
</div>
</div>
</div>
<div class="col-6 col-lg-2">
<div class="card h-100">
<div class="card-body">
<div class="text-muted"><span class="status-dot dot-failed me-2" aria-hidden="true"></span>Failed</div>
<div class="display-6 mb-0">{{ jobs_error_count }}</div>
</div>
</div>
</div>
<div class="col-6 col-lg-2">
<div class="card h-100">
<div class="card-body">
<div class="text-muted"><span class="status-dot dot-missed me-2" aria-hidden="true"></span>Missed</div>
<div class="display-6 mb-0">{{ jobs_missed_count }}</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="card mb-4">
<div class="card-header">Legend</div>
<div class="card-body">
<div class="d-flex flex-column gap-2 small">
<div><span class="status-dot dot-success me-2" aria-hidden="true"></span><strong>Success</strong> — job run completed successfully</div>
<div><span class="status-dot dot-failed me-2" aria-hidden="true"></span><strong>Failed</strong> — job run failed, action required</div>
<div><span class="status-dot dot-warning me-2" aria-hidden="true"></span><strong>Warning</strong> — job run completed with a warning</div>
<div><span class="status-dot dot-missed me-2" aria-hidden="true"></span><strong>Missed</strong> — job run expected but did not execute</div>
<div><span class="status-dot dot-expected me-2" aria-hidden="true"></span><strong>Expected</strong> — job run not yet due</div>
<div><span class="status-dot dot-override me-2" aria-hidden="true"></span><strong>Success (override)</strong> — marked as successful via override</div>
</div>
</div>
</div>
{% if news_items %} {% if news_items %}
<div class="card mb-4"> <div class="card mb-3">
<div class="card-header d-flex align-items-center justify-content-between"> <div class="card-header d-flex align-items-center justify-content-between py-2">
<span>News</span> <span class="fw-semibold" style="font-size:13px;">News</span>
{% if active_role == 'admin' %} {% if active_role == 'admin' %}
<a class="btn btn-sm btn-outline-secondary" href="{{ url_for('main.settings', section='news') }}">Manage</a> <a class="btn btn-sm btn-outline-secondary" href="{{ url_for('main.settings', section='news') }}">Manage</a>
{% endif %} {% endif %}
</div> </div>
<div class="card-body"> <div class="card-body py-3">
<div class="d-flex flex-column gap-3"> <div class="d-flex flex-column gap-3">
{% for item in news_items %} {% for item in news_items %}
<div class="border rounded p-3"> <div class="border rounded p-3">
@ -122,46 +97,20 @@
</div> </div>
</div> </div>
{% endif %} {% endif %}
<div class="mt-3 small text-muted">
<p>Backupchecks provides a centralized and consistent overview of the health and reliability of all backups within your environment. The platform collects backup results from multiple backup solutions and normalizes them into a single, clear and consistent status model. This enables teams to monitor backup quality across different vendors and environments in a predictable and uniform way.</p>
<p>Backup results are imported and evaluated automatically. Each backup run is analyzed and assigned a status such as Success, Warning, Failed, or Success (override). These statuses are determined by interpreting exit codes, detected error messages, log content, and configured rules, ensuring that the reported outcome reflects the real operational impact rather than raw technical output alone.</p>
<p>The dashboard provides an at-a-glance overview of the current backup situation:</p>
<ul>
<li>A consolidated summary of all monitored backup jobs and their latest known results.</li>
<li>Clear counters for successful, warning, failed, and overridden runs.</li>
<li>Immediate visibility into environments that require attention or follow-up.</li>
</ul>
<p>The Daily Jobs view shows the most recent run per backup job, grouped by customer, backup software, and backup type. This view is intended for high-level monitoring and trend awareness. It reflects the latest state of each job, but it does not replace the daily operational review process.</p>
<p>Daily operational validation is performed from the Run Checks page. This page acts as the primary workspace for reviewing backup runs. All runs that require attention are listed here, allowing operators to systematically review results and decide on the appropriate next step. The main objective of this process is to actively review backup runs and keep the Run Checks page clear.</p>
<p>When reviewing a run, operators assess whether the result is acceptable, requires follow-up, or can be treated as successful. A run can be marked as reviewed once it has been checked, even if additional actions are required. Marking a run as reviewed confirms that the result has been acknowledged and assessed, and prevents it from repeatedly appearing as unprocessed.</p>
<p>If a backup run requires further investigation or corrective action, operators can add a remark or reference an external ticket number. After adding this information, the run can still be marked as reviewed, ensuring that it no longer blocks daily checks.</p>
<p>Reviewed runs that require follow-up retain their status until they are explicitly marked as resolved. The reviewed state remains in place to indicate that the run has been handled operationally, while the resolved state confirms that the underlying issue has been fully addressed.</p>
<p>Overrides can be applied during this process when a warning or error is known, accepted, or considered non-critical. Overrides allow such runs to be treated as successful for reporting and dashboard purposes, while preserving the original messages and maintaining a full audit trail.</p>
<p>The ultimate goal of the Run Checks workflow is to maintain an empty or near-empty Run Checks page.</p>
<p>Backupchecks is designed as a monitoring, validation, and control platform. It does not replace your backup software, but enhances it by adding structured review workflows, consistent reporting, and operational clarity across all backup solutions.</p>
</div>
{% if active_role == 'admin' %} {% if active_role == 'admin' %}
<div class="card mb-4"> <div class="card mb-3">
<div class="card-header"> <div class="card-header py-2"><span class="fw-semibold" style="font-size:13px;">System status</span></div>
System status <div class="card-body py-3">
</div> <div class="row g-2 small">
<div class="card-body"> <div class="col-md-6"><span class="text-muted">Database size:</span> <strong>{{ db_size_human }}</strong></div>
<div class="row mb-2">
<div class="col-md-6"> <div class="col-md-6">
<strong>Database size:</strong> {{ db_size_human }} <span class="text-muted">Free disk space:</span>
</div>
<div class="col-md-6">
<strong>Free disk space:</strong>
{% if free_disk_warning %} {% if free_disk_warning %}
<span class="text-danger fw-bold">{{ free_disk_human }}</span> <strong class="text-danger">{{ free_disk_human }}</strong>
<span class="text-danger">(mail import will be blocked below 2 GB)</span> <span class="text-danger">(mail import will be blocked below 2 GB)</span>
{% else %} {% else %}
{{ free_disk_human }} <strong>{{ free_disk_human }}</strong>
{% endif %} {% endif %}
</div> </div>
</div> </div>
@ -169,4 +118,10 @@
</div> </div>
{% endif %} {% endif %}
<div class="mt-3 small text-muted" style="max-width:900px;">
<p>Backupchecks provides a centralized and consistent overview of the health and reliability of all backups within your environment. The platform collects backup results from multiple backup solutions and normalizes them into a single, clear and consistent status model. This enables teams to monitor backup quality across different vendors and environments in a predictable and uniform way.</p>
<p>Backup results are imported and evaluated automatically. Each backup run is analyzed and assigned a status such as Success, Warning, Failed, or Success (override). These statuses are determined by interpreting exit codes, detected error messages, log content, and configured rules, ensuring that the reported outcome reflects the real operational impact rather than raw technical output alone.</p>
<p>Daily operational validation is performed from the Run Checks page. This page acts as the primary workspace for reviewing backup runs. All runs that require attention are listed here, allowing operators to systematically review results and decide on the appropriate next step. The main objective of this process is to actively review backup runs and keep the Run Checks page clear.</p>
</div>
{% endblock %} {% endblock %}