Clickable home page stat cards; clean up nav; add buttons on list pages; move export/import to home
This commit is contained in:
@@ -61,6 +61,27 @@ function metaSelectChanged(sel, inputId) {
|
||||
if (e.key === 'Escape' && modal.classList.contains('open')) closeModal();
|
||||
});
|
||||
|
||||
// Apply URL params as initial filter state on battery_list and device_list
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
var params = new URLSearchParams(window.location.search);
|
||||
var statusParam = params.get('status');
|
||||
if (statusParam !== null) {
|
||||
var statusSel = document.getElementById('filter-status');
|
||||
if (statusSel && typeof applyFilters === 'function') {
|
||||
statusSel.value = statusParam;
|
||||
applyFilters();
|
||||
}
|
||||
}
|
||||
var fillParam = params.get('fill');
|
||||
if (fillParam !== null) {
|
||||
var fillSel = document.getElementById('filter-fill');
|
||||
if (fillSel && typeof applyDeviceFilters === 'function') {
|
||||
fillSel.value = fillParam;
|
||||
applyDeviceFilters();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Global handler: forms with data-confirm attribute
|
||||
document.addEventListener('submit', function(e) {
|
||||
var form = e.target;
|
||||
|
||||
@@ -2,9 +2,10 @@
|
||||
"name": "Battery Tracker",
|
||||
"short_name": "Batteries",
|
||||
"start_url": "/",
|
||||
"scope": "/",
|
||||
"display": "standalone",
|
||||
"background_color": "#ffffff",
|
||||
"theme_color": "#2563eb",
|
||||
"theme_color": "#1e40af",
|
||||
"icons": [
|
||||
{ "src": "/static/icon-192.png", "sizes": "192x192", "type": "image/png" },
|
||||
{ "src": "/static/icon-512.png", "sizes": "512x512", "type": "image/png" }
|
||||
|
||||
@@ -144,6 +144,11 @@
|
||||
.badge-retired { background: var(--badge-retired-bg); color: var(--badge-retired-text); }
|
||||
.badge-warning { background: var(--badge-warning-bg); color: var(--badge-warning-text); }
|
||||
|
||||
/* Clickable stat cards on home page */
|
||||
.stat-link { display: block; flex: 1; min-width: 120px; text-decoration: none; }
|
||||
.stat-link:hover { text-decoration: none; }
|
||||
.stat-link:hover .card { box-shadow: 0 2px 8px rgba(0,0,0,.18); }
|
||||
|
||||
/* Buttons */
|
||||
.btn { display: inline-block; padding: 0.4rem 0.9rem; border-radius: 4px; border: none; cursor: pointer; font-size: 0.875rem; font-family: inherit; text-decoration: none; }
|
||||
.btn:hover { text-decoration: none; }
|
||||
@@ -300,4 +305,53 @@
|
||||
.form-actions { flex-direction: column; align-items: stretch; }
|
||||
.form-actions .btn,
|
||||
.form-actions button { width: 100%; text-align: center; }
|
||||
|
||||
/* ── Mobile: html bg matches nav so status bar area has no color gap ── */
|
||||
html { background: #1e40af; }
|
||||
|
||||
/* ── Mobile: title-bar nav fills behind status bar, links hidden ── */
|
||||
nav:not(.bottom-nav) {
|
||||
padding-top: calc(0.4rem + env(safe-area-inset-top));
|
||||
padding-bottom: 0.4rem;
|
||||
justify-content: center;
|
||||
}
|
||||
nav:not(.bottom-nav) a:not(.brand) { display: none; }
|
||||
nav:not(.bottom-nav) .brand { margin-right: 0; font-size: 0.95rem; }
|
||||
body { padding-bottom: 4.5rem; }
|
||||
|
||||
.bottom-nav {
|
||||
position: fixed;
|
||||
bottom: 0; left: 0; right: 0;
|
||||
display: flex;
|
||||
background: #1e40af;
|
||||
border-top: 1px solid #1d4ed8;
|
||||
z-index: 100;
|
||||
padding-bottom: env(safe-area-inset-bottom);
|
||||
}
|
||||
.bottom-nav-item {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 0.5rem 0.25rem 0.4rem;
|
||||
color: #bfdbfe;
|
||||
text-decoration: none;
|
||||
font-size: 0.65rem;
|
||||
gap: 0.2rem;
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
}
|
||||
.bottom-nav-item.active { color: #fff; }
|
||||
.bottom-nav-item svg { display: block; }
|
||||
}
|
||||
|
||||
/* ── Desktop: hide bottom nav ── */
|
||||
@media (min-width: 641px) {
|
||||
.bottom-nav { display: none; }
|
||||
}
|
||||
|
||||
/* ── Dark mode: bottom nav + html background ── */
|
||||
@media (prefers-color-scheme: dark) and (max-width: 640px) {
|
||||
html { background: #0c1a3b; }
|
||||
.bottom-nav { background: #0c1a3b; border-top-color: #1e3a8a; }
|
||||
}
|
||||
|
||||
@@ -20,8 +20,6 @@
|
||||
<a class="brand" href="{{ url_for('home') }}">Battery Tracker</a>
|
||||
<a href="{{ url_for('battery_list') }}">Batteries</a>
|
||||
<a href="{{ url_for('device_list') }}">Devices</a>
|
||||
<a href="{{ url_for('battery_add') }}">+ Battery</a>
|
||||
<a href="{{ url_for('device_add') }}">+ Device</a>
|
||||
</nav>
|
||||
|
||||
<div class="container">
|
||||
@@ -34,19 +32,6 @@
|
||||
{% block content %}{% endblock %}
|
||||
</div>
|
||||
|
||||
<footer style="text-align:center;padding:1.25rem 1rem 1.5rem;margin-top:1rem;
|
||||
border-top:1px solid var(--border);font-size:0.8rem;">
|
||||
<a href="{{ url_for('export_page') }}"
|
||||
style="color:var(--text-muted);text-decoration:none;"
|
||||
onmouseover="this.style.textDecoration='underline'"
|
||||
onmouseout="this.style.textDecoration='none'">Export data</a>
|
||||
<span style="color:var(--text-muted);margin:0 0.5rem;">·</span>
|
||||
<a href="{{ url_for('import_page') }}"
|
||||
style="color:var(--text-muted);text-decoration:none;"
|
||||
onmouseover="this.style.textDecoration='underline'"
|
||||
onmouseout="this.style.textDecoration='none'">Import data</a>
|
||||
</footer>
|
||||
|
||||
<div id="confirm-modal" role="dialog" aria-modal="true">
|
||||
<div id="confirm-modal-box">
|
||||
<p id="confirm-modal-msg"></p>
|
||||
@@ -79,18 +64,6 @@
|
||||
</svg>
|
||||
<span>Devices</span>
|
||||
</a>
|
||||
<a href="{{ url_for('battery_add') }}" class="bottom-nav-item{% if ep == 'battery_add' %} active{% endif %}">
|
||||
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
|
||||
<rect x="2" y="7" width="16" height="10" rx="2"/><path d="M22 11v2"/><line x1="10" y1="11" x2="10" y2="15"/><line x1="8" y1="13" x2="12" y2="13"/>
|
||||
</svg>
|
||||
<span>+ Battery</span>
|
||||
</a>
|
||||
<a href="{{ url_for('device_add') }}" class="bottom-nav-item{% if ep == 'device_add' %} active{% endif %}">
|
||||
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
|
||||
<circle cx="12" cy="12" r="10"/><line x1="12" y1="8" x2="12" y2="16"/><line x1="8" y1="12" x2="16" y2="12"/>
|
||||
</svg>
|
||||
<span>+ Device</span>
|
||||
</a>
|
||||
</nav>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -5,7 +5,8 @@
|
||||
<h1>Batteries</h1>
|
||||
|
||||
<div class="card">
|
||||
<div style="display:flex;justify-content:flex-end;margin-bottom:0.5rem;">
|
||||
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:0.5rem;">
|
||||
<a class="btn btn-primary btn-sm" href="{{ url_for('battery_add') }}">+ Add Battery</a>
|
||||
<div style="position:relative;">
|
||||
<button type="button" id="col-picker-btn" class="btn btn-sm btn-secondary">Columns ▾</button>
|
||||
<div id="col-picker-panel" style="display:none;position:absolute;right:0;top:calc(100% + 4px);background:#fff;border:1px solid #d1d5db;border-radius:6px;padding:0.75rem 1rem;z-index:100;box-shadow:0 4px 8px rgba(0,0,0,0.1);min-width:180px;">
|
||||
|
||||
@@ -5,6 +5,9 @@
|
||||
<h1>Devices</h1>
|
||||
|
||||
<div class="card">
|
||||
<div style="display:flex;justify-content:flex-start;margin-bottom:0.5rem;">
|
||||
<a class="btn btn-primary btn-sm" href="{{ url_for('device_add') }}">+ Add Device</a>
|
||||
</div>
|
||||
<div id="device-filter-bar" style="display:flex;gap:0.5rem;flex-wrap:wrap;align-items:center;margin-bottom:0.75rem;">
|
||||
<select id="filter-type" onchange="applyDeviceFilters()"
|
||||
style="padding:0.25rem 0.5rem;font-size:0.85rem;border:1px solid #cbd5e1;border-radius:4px;">
|
||||
|
||||
+31
-13
@@ -6,22 +6,30 @@
|
||||
|
||||
<h2 style="font-size:1rem;font-weight:600;color:var(--text-muted);text-transform:uppercase;letter-spacing:0.05em;margin:0 0 0.6rem;">Batteries</h2>
|
||||
<div style="display:flex;gap:1rem;flex-wrap:wrap;margin-bottom:1.25rem;">
|
||||
<div class="card" style="flex:1;min-width:120px;text-align:center;">
|
||||
<a class="stat-link" href="{{ url_for('battery_list') }}?status=">
|
||||
<div class="card" style="text-align:center;">
|
||||
<div style="font-size:1.8rem;font-weight:700;">{{ total_batteries }}</div>
|
||||
<div class="text-muted">Total</div>
|
||||
</div>
|
||||
<div class="card" style="flex:1;min-width:120px;text-align:center;">
|
||||
</a>
|
||||
<a class="stat-link" href="{{ url_for('battery_list') }}?status=available">
|
||||
<div class="card" style="text-align:center;">
|
||||
<div style="font-size:1.8rem;font-weight:700;color:var(--count-available);">{{ avail_count }}</div>
|
||||
<div class="text-muted">Available</div>
|
||||
</div>
|
||||
<div class="card" style="flex:1;min-width:120px;text-align:center;">
|
||||
</a>
|
||||
<a class="stat-link" href="{{ url_for('battery_list') }}?status=installed">
|
||||
<div class="card" style="text-align:center;">
|
||||
<div style="font-size:1.8rem;font-weight:700;color:var(--count-installed);">{{ installed_count }}</div>
|
||||
<div class="text-muted">Installed</div>
|
||||
</div>
|
||||
<div class="card" style="flex:1;min-width:120px;text-align:center;">
|
||||
</a>
|
||||
<a class="stat-link" href="{{ url_for('battery_list') }}?status=retired">
|
||||
<div class="card" style="text-align:center;">
|
||||
<div style="font-size:1.8rem;font-weight:700;color:var(--count-retired);">{{ retired_count }}</div>
|
||||
<div class="text-muted">Retired</div>
|
||||
</div>
|
||||
</a>
|
||||
{% if ha_enabled and needs_attention.low_pct %}
|
||||
<a href="#needs-attention"
|
||||
onclick="var d=document.getElementById('needs-attention');d.open=true;"
|
||||
@@ -36,22 +44,30 @@
|
||||
|
||||
<h2 style="font-size:1rem;font-weight:600;color:var(--text-muted);text-transform:uppercase;letter-spacing:0.05em;margin:0 0 0.6rem;">Devices</h2>
|
||||
<div style="display:flex;gap:1rem;flex-wrap:wrap;margin-bottom:1.25rem;">
|
||||
<div class="card" style="flex:1;min-width:120px;text-align:center;">
|
||||
<a class="stat-link" href="{{ url_for('device_list') }}">
|
||||
<div class="card" style="text-align:center;">
|
||||
<div style="font-size:1.8rem;font-weight:700;">{{ total_devices }}</div>
|
||||
<div class="text-muted">Total</div>
|
||||
</div>
|
||||
<div class="card" style="flex:1;min-width:120px;text-align:center;">
|
||||
</a>
|
||||
<a class="stat-link" href="{{ url_for('device_list') }}?fill=full">
|
||||
<div class="card" style="text-align:center;">
|
||||
<div style="font-size:1.8rem;font-weight:700;color:var(--count-installed);">{{ full_devices }}</div>
|
||||
<div class="text-muted">Full</div>
|
||||
</div>
|
||||
<div class="card" style="flex:1;min-width:120px;text-align:center;">
|
||||
</a>
|
||||
<a class="stat-link" href="{{ url_for('device_list') }}?fill=partial">
|
||||
<div class="card" style="text-align:center;">
|
||||
<div style="font-size:1.8rem;font-weight:700;color:var(--count-available);">{{ partial_devices }}</div>
|
||||
<div class="text-muted">Partial</div>
|
||||
</div>
|
||||
<div class="card" style="flex:1;min-width:120px;text-align:center;{% if empty_devices > 0 %}border:2px solid #f59e0b;{% endif %}">
|
||||
</a>
|
||||
<a class="stat-link" href="{{ url_for('device_list') }}?fill=empty">
|
||||
<div class="card" style="text-align:center;{% if empty_devices > 0 %}border:2px solid #f59e0b;{% endif %}">
|
||||
<div style="font-size:1.8rem;font-weight:700;{% if empty_devices > 0 %}color:#f59e0b;{% else %}color:var(--count-retired);{% endif %}">{{ empty_devices }}</div>
|
||||
<div class="text-muted">Empty</div>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
{% if total_charges %}
|
||||
@@ -96,11 +112,13 @@
|
||||
</details>
|
||||
{% endif %}
|
||||
|
||||
<div style="display:flex;gap:0.75rem;flex-wrap:wrap;margin-top:0.5rem;">
|
||||
<a class="btn btn-primary" href="{{ url_for('battery_add') }}">+ Add Battery</a>
|
||||
<a class="btn btn-secondary" href="{{ url_for('battery_list') }}">View All Batteries</a>
|
||||
<a class="btn btn-secondary" href="{{ url_for('device_list') }}">View All Devices</a>
|
||||
<a class="btn btn-secondary" href="{{ url_for('device_add') }}">+ Add Device</a>
|
||||
<div style="display:grid;grid-template-columns:1fr 1fr;gap:0.75rem;margin-top:0.5rem;max-width:480px;">
|
||||
<a class="btn btn-primary" style="text-align:center;" href="{{ url_for('battery_add') }}">+ Add Battery</a>
|
||||
<a class="btn btn-secondary" style="text-align:center;" href="{{ url_for('battery_list') }}">View All Batteries</a>
|
||||
<a class="btn btn-primary" style="text-align:center;" href="{{ url_for('device_add') }}">+ Add Device</a>
|
||||
<a class="btn btn-secondary" style="text-align:center;" href="{{ url_for('device_list') }}">View All Devices</a>
|
||||
<a class="btn btn-secondary" style="text-align:center;" href="{{ url_for('export_page') }}">Export Data</a>
|
||||
<a class="btn btn-secondary" style="text-align:center;" href="{{ url_for('import_page') }}">Import Data</a>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
Reference in New Issue
Block a user