feat(dashboard): 3-col layout with Overview stats, speed chart fixes
- base.html: fix notification badge positioning (inline style, no overflow clip); remove py-4 from main; update footer text - index.html: split bottom row into 3 cols (GPS / Overview / Speed); Overview card with 6 live stats from /api/stats; speed chart auto-expands on desktop; shown.bs.collapse triggers speedChart.resize() Co-authored-by: Copilot <[email protected]>
This commit is contained in:
+69
-9
@@ -91,9 +91,9 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{# ── GPS map (with inline coords footer) ─────────────────────────────── #}
|
||||
{# ── GPS map + Stats + Speed history ──────────────────────────────────── #}
|
||||
<div class="row g-3 mb-3">
|
||||
<div class="col-lg-5 col-xl-4">
|
||||
<div class="col-lg-4 col-xl-3">
|
||||
<div class="card shadow-sm h-100">
|
||||
<div class="card-header py-2">
|
||||
<h6 class="text-primary fw-bold m-0">Live GPS</h6>
|
||||
@@ -113,16 +113,50 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-7 col-xl-8">
|
||||
<div class="col-lg-3 col-xl-2">
|
||||
<div class="card shadow-sm h-100">
|
||||
<div class="card-header py-2 d-flex align-items-center gap-3">
|
||||
<div class="card-header py-2">
|
||||
<h6 class="text-primary fw-bold m-0">Overview</h6>
|
||||
</div>
|
||||
<div class="card-body py-3 px-3" id="stats-body">
|
||||
<div class="row g-3">
|
||||
<div class="col-6 col-lg-12">
|
||||
<div class="text-xs text-muted fw-bold text-uppercase mb-1">Trips</div>
|
||||
<div class="fw-bold fs-5 text-dark" id="stat-trips">—</div>
|
||||
</div>
|
||||
<div class="col-6 col-lg-12">
|
||||
<div class="text-xs text-muted fw-bold text-uppercase mb-1">Total distance</div>
|
||||
<div class="fw-bold fs-5 text-dark" id="stat-km">—</div>
|
||||
</div>
|
||||
<div class="col-6 col-lg-12">
|
||||
<div class="text-xs text-muted fw-bold text-uppercase mb-1">Drive time</div>
|
||||
<div class="fw-bold fs-5 text-dark" id="stat-time">—</div>
|
||||
</div>
|
||||
<div class="col-6 col-lg-12">
|
||||
<div class="text-xs text-muted fw-bold text-uppercase mb-1">Top speed ever</div>
|
||||
<div class="fw-bold fs-5 text-dark" id="stat-topspeed">—</div>
|
||||
</div>
|
||||
<div class="col-6 col-lg-12">
|
||||
<div class="text-xs text-muted fw-bold text-uppercase mb-1">Videos on Pi</div>
|
||||
<div class="fw-bold fs-5 text-dark" id="stat-videos">—</div>
|
||||
</div>
|
||||
<div class="col-6 col-lg-12">
|
||||
<div class="text-xs text-muted fw-bold text-uppercase mb-1">Active DTC</div>
|
||||
<div class="fw-bold fs-5" id="stat-dtc">—</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-5 col-xl-7">
|
||||
<div class="card shadow-sm h-100">
|
||||
<div class="card-header py-2 d-flex align-items-center gap-2">
|
||||
<h6 class="text-primary fw-bold m-0 me-auto">Speed history</h6>
|
||||
{# Live speed badge — visible even when graph is collapsed #}
|
||||
<span class="text-xs text-muted fw-bold text-uppercase">Now</span>
|
||||
<span id="speed-now-badge" class="fw-bold text-dark" style="min-width:3.5rem;text-align:right;">—</span>
|
||||
<span class="text-xs text-muted fw-bold text-uppercase ms-2">Max</span>
|
||||
<span id="speed-max-badge" class="fw-bold text-danger" style="min-width:3.5rem;text-align:right;">—</span>
|
||||
<button class="btn btn-link btn-sm p-0 text-muted ms-2" id="speed-graph-toggle" type="button"
|
||||
<span id="speed-now-badge" class="fw-bold text-dark" style="min-width:3rem;text-align:right;">—</span>
|
||||
<span class="text-xs text-muted fw-bold text-uppercase ms-2 speed-max-group">Max</span>
|
||||
<span id="speed-max-badge" class="fw-bold text-danger speed-max-group" style="min-width:3rem;text-align:right;">—</span>
|
||||
<button class="btn btn-link btn-sm p-0 text-muted ms-1" id="speed-graph-toggle" type="button"
|
||||
aria-expanded="false" aria-controls="speed-graph-body">
|
||||
<i class="fas fa-chevron-down"></i>
|
||||
</button>
|
||||
@@ -158,11 +192,37 @@
|
||||
btn.setAttribute('aria-expanded', 'true');
|
||||
btn.querySelector('i').className = 'fas fa-chevron-up';
|
||||
});
|
||||
body.addEventListener('shown.bs.collapse', function () {
|
||||
if (window.speedChart) window.speedChart.resize();
|
||||
});
|
||||
body.addEventListener('hide.bs.collapse', function () {
|
||||
btn.setAttribute('aria-expanded', 'false');
|
||||
btn.querySelector('i').className = 'fas fa-chevron-down';
|
||||
});
|
||||
btn.addEventListener('click', function () { bsCollapse.toggle(); });
|
||||
// Auto-expand on desktop; stay collapsed on mobile
|
||||
if (window.innerWidth >= 768) {
|
||||
bsCollapse.show();
|
||||
}
|
||||
})();
|
||||
|
||||
// Overview stats card
|
||||
(function () {
|
||||
function fmtDuration(s) {
|
||||
const h = Math.floor(s / 3600);
|
||||
const m = Math.floor((s % 3600) / 60);
|
||||
return h > 0 ? h + 'h ' + m + 'm' : m + 'm';
|
||||
}
|
||||
fetch('/api/stats').then(r => r.json()).then(d => {
|
||||
document.getElementById('stat-trips').textContent = d.trip_count;
|
||||
document.getElementById('stat-km').textContent = d.total_km.toFixed(1) + ' km';
|
||||
document.getElementById('stat-time').textContent = d.total_duration_s > 0 ? fmtDuration(d.total_duration_s) : '—';
|
||||
document.getElementById('stat-topspeed').textContent = d.all_time_max_speed > 0 ? d.all_time_max_speed + ' km/h' : '—';
|
||||
document.getElementById('stat-videos').textContent = d.video_count;
|
||||
const dtcEl = document.getElementById('stat-dtc');
|
||||
dtcEl.textContent = d.active_dtc;
|
||||
dtcEl.className = 'fw-bold fs-5 ' + (d.active_dtc > 0 ? 'text-danger' : 'text-success');
|
||||
}).catch(() => {});
|
||||
})();
|
||||
</script>
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user