Redesign BP tracker with lightweight views and unobtrusive save confirmations
This commit is contained in:
+34
-114
@@ -1,114 +1,34 @@
|
||||
{% extends "_layout.html" %}
|
||||
{% block content %}
|
||||
<div class="max-w-5xl mx-auto p-4 space-y-6">
|
||||
<!-- Header Section with "Add New Reading" Button -->
|
||||
<div class="flex justify-between items-center">
|
||||
<h1 class="text-2xl font-bold text-gray-800">Dashboard</h1>
|
||||
<a rel="prefetch" href="{{ url_for('reading.add_reading') }}"
|
||||
class="bg-primary-600 text-white px-4 py-2 rounded shadow hover:bg-primary-700">
|
||||
+ Add New Reading
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- Weekly Summary -->
|
||||
<div class="bg-gradient-to-r from-primary-500 to-primary-700 text-white p-6 rounded-xl shadow-md">
|
||||
<h3 class="text-lg font-bold">Weekly Summary</h3>
|
||||
<div class="flex justify-between mt-4">
|
||||
<div>
|
||||
<p class="text-sm font-semibold">Systolic Average</p>
|
||||
<p class="text-2xl">{{ systolic_avg }} <span class="text-base">mmHg</span></p>
|
||||
</div>
|
||||
<div>
|
||||
<p class="text-sm font-semibold">Diastolic Average</p>
|
||||
<p class="text-2xl">{{ diastolic_avg }} <span class="text-base">mmHg</span></p>
|
||||
</div>
|
||||
<div>
|
||||
<p class="text-sm font-semibold">Heart Rate Average</p>
|
||||
<p class="text-2xl">{{ heart_rate_avg }} <span class="text-base">bpm</span></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Progress Badges -->
|
||||
<div>
|
||||
<div class="flex flex-wrap gap-4">
|
||||
{% for badge in badges %}
|
||||
<div class="bg-green-100 text-green-800 px-4 py-2 rounded shadow text-sm font-medium">
|
||||
{{ badge }}
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
<div class="max-w-5xl mx-auto">
|
||||
<!-- Tabs -->
|
||||
<div class="flex border-b mb-4 overflow-x-auto" id="dashboard-tabs">
|
||||
<button hx-get="{{ url_for('main.dashboard_list') }}" hx-target="#dashboard-content"
|
||||
class="tab-btn px-4 py-2 text-sm font-medium border-b-2 whitespace-nowrap {{ 'border-primary-600 text-primary-600' if active_view == 'list' else 'border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300' }}">List
|
||||
View</button>
|
||||
<button hx-get="{{ url_for('main.dashboard_table') }}" hx-target="#dashboard-content"
|
||||
class="tab-btn px-4 py-2 text-sm font-medium border-b-2 whitespace-nowrap {{ 'border-primary-600 text-primary-600' if active_view == 'table' else 'border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300' }}">Table
|
||||
View</button>
|
||||
<button hx-get="{{ url_for('main.dashboard_weekly') }}" hx-target="#dashboard-content"
|
||||
class="tab-btn px-4 py-2 text-sm font-medium border-b-2 whitespace-nowrap {{ 'border-primary-600 text-primary-600' if active_view == 'weekly' else 'border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300' }}">Weekly
|
||||
View</button>
|
||||
<button hx-get="{{ url_for('main.dashboard_monthly') }}" hx-target="#dashboard-content"
|
||||
class="tab-btn px-4 py-2 text-sm font-medium border-b-2 whitespace-nowrap {{ 'border-primary-600 text-primary-600' if active_view == 'monthly' else 'border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300' }}">Monthly
|
||||
View</button>
|
||||
<button hx-get="{{ url_for('main.dashboard_graph') }}" hx-target="#dashboard-content"
|
||||
class="tab-btn px-4 py-2 text-sm font-medium border-b-2 whitespace-nowrap {{ 'border-primary-600 text-primary-600' if active_view == 'graph' else 'border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300' }}">Graph
|
||||
View</button>
|
||||
</div>
|
||||
|
||||
<!-- Dashboard Content Target Area for HTMX -->
|
||||
<div id="dashboard-content" hx-get="{{ url_for('main.dashboard_list') }}" hx-trigger="load">
|
||||
<div class="flex justify-center items-center p-12">
|
||||
<div class="animate-spin rounded-full h-8 w-8 border-b-2 border-primary-600"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// Use event delegation for dashboard interactions to survive DIY Turbo page replacements
|
||||
document.addEventListener('click', (e) => {
|
||||
// Filter Form Toggle
|
||||
const filterBtn = e.target.closest('#filter-btn');
|
||||
if (filterBtn) {
|
||||
const filterForm = document.getElementById('filter-form');
|
||||
const iconClosed = document.getElementById('filter-icon-closed');
|
||||
const iconOpen = document.getElementById('filter-icon-open');
|
||||
|
||||
if (filterForm) {
|
||||
const isHidden = filterForm.classList.contains('hidden');
|
||||
if (isHidden) {
|
||||
filterForm.classList.remove('hidden');
|
||||
if (iconClosed) iconClosed.classList.add('hidden');
|
||||
if (iconOpen) iconOpen.classList.remove('hidden');
|
||||
} else {
|
||||
filterForm.classList.add('hidden');
|
||||
if (iconClosed) iconClosed.classList.remove('hidden');
|
||||
if (iconOpen) iconOpen.classList.add('hidden');
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Tabs
|
||||
const tabBtn = e.target.closest('.tab-btn');
|
||||
if (tabBtn) {
|
||||
const tabBtns = document.querySelectorAll('.tab-btn');
|
||||
tabBtns.forEach(b => {
|
||||
b.classList.remove('border-primary-600', 'text-primary-600');
|
||||
});
|
||||
tabBtn.classList.add('border-primary-600', 'text-primary-600');
|
||||
// The click will still bubble up and be caught by the Micro-HTMX logic in _layout.html
|
||||
return;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
{% extends '_layout.html' %}
|
||||
{% from 'components.html' import reading_rows %}
|
||||
{% block title %}{{ view|title }} · BP Tracker{% endblock %}
|
||||
{% block content %}
|
||||
<div class="page-heading"><div><p class="eyebrow">YOUR DAILY PICTURE</p><h1>{{ view|title }}</h1><p class="muted">{{ 'A clearer view of your blood pressure.' if view == 'overview' else 'Every reading, in one place.' if view == 'history' else 'See how your readings change over time.' }}</p></div><a class="button primary add-action" href="{{ url_for('reading.add_reading') }}"><span aria-hidden="true">+</span> Add reading</a></div>
|
||||
{% if not calendar %}{% include 'partials/range_filter.html' %}{% endif %}
|
||||
{% if view == 'overview' %}
|
||||
<section class="summary-grid" aria-label="Summary">
|
||||
<article class="card latest-card"><div class="card-label"><h2>Latest reading</h2><span class="dot" aria-hidden="true"></span></div>
|
||||
{% if latest %}<p class="big-number">{{ latest.systolic }}<span class="slash"> / </span>{{ latest.diastolic }} <span class="unit">mmHg</span></p><p class="latest-meta">{{ latest.heart_rate }} bpm <span>·</span> {{ latest.local_timestamp.strftime('%d %b, %I:%M %p') }}</p><p class="card-foot">Most recent, across all dates</p>
|
||||
{% else %}<p class="empty-title">Your first reading starts here</p><p class="muted">Record your blood pressure and pulse to get started.</p>{% endif %}</article>
|
||||
<article class="card"><h2>Period average</h2>{% if stats.count %}<p class="big-number">{{ stats.systolic }}<span class="slash"> / </span>{{ stats.diastolic }}</p><p class="muted">mmHg · {{ stats.pulse }} bpm average pulse</p>{% else %}<p class="empty-title">No readings yet</p><p class="muted">Averages appear when you add a reading.</p>{% endif %}<p class="card-foot">{{ selected.start.strftime('%d %b') }} – {{ selected.end.strftime('%d %b %Y') }}</p></article>
|
||||
<article class="card count-card"><h2>Readings recorded</h2><p class="big-number">{{ stats.count }}</p><p class="muted">In the selected period</p><p class="card-foot">Times shown in {{ timezone_name }}</p></article>
|
||||
</section>
|
||||
{% endif %}
|
||||
{% if view in ['overview', 'trends'] %}{% include 'partials/trend.html' %}{% endif %}
|
||||
{% if view == 'history' %}
|
||||
<div class="section-heading"><nav class="segmented" aria-label="History display"><a href="{{ url_for('main.dashboard', view='history', **selected.params) }}" {% if not calendar %}aria-current="page"{% endif %}>Readings</a><a href="{{ url_for('main.dashboard', view='history', mode='calendar') }}" {% if calendar %}aria-current="page"{% endif %}>Calendar</a></nav><span class="muted small">{{ stats.count }} readings · {{ timezone_name }}</span></div>
|
||||
{% endif %}
|
||||
{% if calendar %}
|
||||
<section class="card calendar-card">
|
||||
<div class="section-heading"><h2>{{ selected.start.strftime('%d %b') }} – {{ selected.end.strftime('%d %b %Y') }}</h2><div class="actions"><a class="button" aria-label="Previous {{ calendar.unit }}" href="{{ url_for('main.dashboard', view='history', mode='calendar', unit=calendar.unit, anchor=calendar.prev) }}">←</a><a class="button" aria-label="Next {{ calendar.unit }}" href="{{ url_for('main.dashboard', view='history', mode='calendar', unit=calendar.unit, anchor=calendar.next) }}">→</a></div></div>
|
||||
<form class="calendar-controls" action="{{ url_for('main.dashboard') }}" method="get"><input type="hidden" name="view" value="history"><input type="hidden" name="mode" value="calendar"><div class="field"><label for="unit">Display</label><select name="unit" id="unit"><option value="month" {% if calendar.unit == 'month' %}selected{% endif %}>Month</option><option value="week" {% if calendar.unit == 'week' %}selected{% endif %}>Week</option></select></div><div class="field"><label for="anchor">Containing date</label><input id="anchor" type="date" name="anchor" value="{{ calendar.anchor }}" required></div><button class="button" type="submit">Go</button></form>
|
||||
<p class="muted small">Daily averages in mmHg. Select a day to see its readings.</p>
|
||||
<div class="calendar-grid" style="--start-day:{{ selected.start.weekday() + 1 }}">{% for day in calendar.days %}<a class="calendar-day {{ 'has-readings' if day.data else '' }}" href="{{ url_for('main.dashboard', view='history', start_date=day.date.isoformat(), end_date=day.date.isoformat()) }}"><span class="muted small">{{ day.date.strftime('%a') }}</span><strong>{{ day.date.day }}</strong>{% if day.data %}<span>{{ day.data.systolic }} / {{ day.data.diastolic }}</span><small>{{ day.data.count }} reading{{ 's' if day.data.count != 1 else '' }}</small>{% else %}<small>No readings</small>{% endif %}</a>{% endfor %}</div>
|
||||
</section>
|
||||
{% elif view in ['overview', 'history'] %}
|
||||
<section class="card history-card"><div class="section-heading"><div><h2>{{ 'Recent readings' if view == 'overview' else 'Reading history' }}</h2><p class="muted small">Personal thresholds: {{ sys_threshold }}/{{ dia_threshold }} mmHg · <a href="{{ url_for('user.profile') }}">Adjust in Settings</a></p></div>{% if view == 'overview' %}<a class="text-link" href="{{ url_for('main.dashboard', view='history', **selected.params) }}">View all <span aria-hidden="true">→</span></a>{% endif %}</div>
|
||||
{{ reading_rows(readings, sys_threshold, dia_threshold) }}
|
||||
{% if pagination and pagination.pages > 1 %}<nav class="pagination" aria-label="History pages">{% if pagination.has_prev %}<a class="button" href="{{ url_for('main.dashboard', view='history', page=pagination.prev_num, **selected.params) }}">← Previous</a>{% endif %}<span>Page {{ pagination.page }} of {{ pagination.pages }}</span>{% if pagination.has_next %}<a class="button" href="{{ url_for('main.dashboard', view='history', page=pagination.next_num, **selected.params) }}">Next →</a>{% endif %}</nav>{% endif %}
|
||||
</section>
|
||||
{% endif %}
|
||||
<div class="report-links"><span class="muted small">Keep a copy of this period</span><a href="{{ url_for('data.export_data', **selected.params) }}" download>Download CSV</a><a href="{{ url_for('main.report', **selected.params) }}">Printable summary ↗</a></div>
|
||||
{% endblock %}
|
||||
|
||||
Reference in New Issue
Block a user