Load dashboard displays using htmx
This commit is contained in:
64
app/templates/partials/dashboard_graph.html
Normal file
64
app/templates/partials/dashboard_graph.html
Normal file
@@ -0,0 +1,64 @@
|
||||
<div class="space-y-6">
|
||||
<!-- Blood Pressure Graph -->
|
||||
<div>
|
||||
<h3 class="text-sm font-semibold text-gray-600 mb-2">Blood Pressure (mmHg)</h3>
|
||||
<svg viewBox="0 0 600 250" xmlns="http://www.w3.org/2000/svg" class="w-full">
|
||||
<!-- Axes -->
|
||||
<line x1="50" y1="200" x2="550" y2="200" stroke="black" stroke-width="1" /> <!-- X-axis -->
|
||||
<line x1="50" y1="20" x2="50" y2="200" stroke="black" stroke-width="1" /> <!-- Y-axis -->
|
||||
|
||||
<!-- Y-axis Labels (Blood Pressure Values) -->
|
||||
{% for value in range(50, 201, 50) %}
|
||||
<text x="40" y="{{ 200 - (value / 200 * 180) }}" font-size="10" text-anchor="end">{{ value }}</text>
|
||||
{% endfor %}
|
||||
|
||||
<!-- X-axis Labels (Timestamps) -->
|
||||
{% for i in range(timestamps|length) %}
|
||||
<text x="{{ 50 + i * 50 }}" y="215" font-size="10" text-anchor="middle">{{ timestamps[i] }}</text>
|
||||
{% endfor %}
|
||||
|
||||
<!-- Graph Lines -->
|
||||
<!-- Systolic Line -->
|
||||
<polyline fill="none" stroke="blue" stroke-width="2"
|
||||
points="{% for i in range(timestamps|length) %}{{ 50 + i * 50 }},{{ 200 - (systolic[i] / 200 * 180) }} {% endfor %}" />
|
||||
|
||||
<!-- Diastolic Line -->
|
||||
<polyline fill="none" stroke="red" stroke-width="2"
|
||||
points="{% for i in range(timestamps|length) %}{{ 50 + i * 50 }},{{ 200 - (diastolic[i] / 200 * 180) }} {% endfor %}" />
|
||||
|
||||
<!-- Axis Labels -->
|
||||
<text x="25" y="110" font-size="12" transform="rotate(-90, 25, 110)" text-anchor="middle">Blood Pressure
|
||||
(mmHg)</text>
|
||||
<text x="300" y="240" font-size="12" text-anchor="middle">Date</text>
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<!-- Heart Rate Graph -->
|
||||
<div>
|
||||
<h3 class="text-sm font-semibold text-gray-600 mb-2">Heart Rate (bpm)</h3>
|
||||
<svg viewBox="0 0 600 250" xmlns="http://www.w3.org/2000/svg" class="w-full">
|
||||
<!-- Axes -->
|
||||
<line x1="50" y1="200" x2="550" y2="200" stroke="black" stroke-width="1" /> <!-- X-axis -->
|
||||
<line x1="50" y1="20" x2="50" y2="200" stroke="black" stroke-width="1" /> <!-- Y-axis -->
|
||||
|
||||
<!-- Y-axis Labels (Heart Rate Values) -->
|
||||
{% for value in range(50, 201, 50) %}
|
||||
<text x="40" y="{{ 200 - (value / 200 * 180) }}" font-size="10" text-anchor="end">{{ value }}</text>
|
||||
{% endfor %}
|
||||
|
||||
<!-- X-axis Labels (Timestamps) -->
|
||||
{% for i in range(timestamps|length) %}
|
||||
<text x="{{ 50 + i * 50 }}" y="215" font-size="10" text-anchor="middle">{{ timestamps[i] }}</text>
|
||||
{% endfor %}
|
||||
|
||||
<!-- Heart Rate Line -->
|
||||
<polyline fill="none" stroke="green" stroke-width="2"
|
||||
points="{% for i in range(timestamps|length) %}{{ 50 + i * 50 }},{{ 200 - (heart_rate[i] / 200 * 180) }} {% endfor %}" />
|
||||
|
||||
<!-- Axis Labels -->
|
||||
<text x="25" y="110" font-size="12" transform="rotate(-90, 25, 110)" text-anchor="middle">Heart Rate
|
||||
(bpm)</text>
|
||||
<text x="300" y="240" font-size="12" text-anchor="middle">Date</text>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
73
app/templates/partials/dashboard_list.html
Normal file
73
app/templates/partials/dashboard_list.html
Normal file
@@ -0,0 +1,73 @@
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-3">
|
||||
{% for reading in readings %}
|
||||
<a href="{{ url_for('reading.edit_reading', reading_id=reading.id) }}"
|
||||
class="bg-white shadow-sm hover:shadow-md rounded-xl p-3 flex justify-between items-center border border-gray-100 transition-all">
|
||||
|
||||
<!-- Left side: Timestamp & BP -->
|
||||
<div>
|
||||
<div class="flex items-center text-gray-400 text-xs mb-1">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="w-3.5 h-3.5 mr-1" fill="none" viewBox="0 0 24 24"
|
||||
stroke="currentColor" stroke-width="2">
|
||||
<path stroke-linecap="round" stroke-linejoin="round"
|
||||
d="M12 8v4l3 3m9-3a9 9 0 1 1-18 0 9 9 0 0 1 18 0z" />
|
||||
</svg>
|
||||
<span title="{{ reading.local_timestamp.strftime('%d %b %Y, %I:%M %p') }}">
|
||||
{{ reading.relative_timestamp }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="flex items-baseline">
|
||||
<span class="text-xl font-bold text-gray-800">{{ reading.systolic }}<span
|
||||
class="text-gray-400 font-normal mx-0.5">/</span>{{ reading.diastolic }}</span>
|
||||
<span class="text-xs text-gray-500 ml-1">mmHg</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Right side: Heart Rate & Icon -->
|
||||
<div class="flex items-center space-x-4">
|
||||
<div class="text-right">
|
||||
<span class="text-xs text-gray-500 block mb-0.5">HR</span>
|
||||
<div class="flex items-baseline justify-end">
|
||||
<span class="text-lg font-bold text-gray-700">{{ reading.heart_rate }}</span>
|
||||
<span class="text-xs text-gray-400 ml-1">bpm</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-gray-300">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2"
|
||||
stroke="currentColor" class="h-4 w-4">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M8.25 4.5l7.5 7.5-7.5 7.5" />
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
{% else %}
|
||||
<div class="col-span-full text-center text-sm text-gray-500">
|
||||
No readings found.
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
<!-- Pagination Controls -->
|
||||
{% if pagination.pages > 1 %}
|
||||
<div class="flex justify-center items-center gap-2 mt-6">
|
||||
{% if pagination.has_prev %}
|
||||
<button hx-get="{{ url_for('main.dashboard_list', page=pagination.prev_num) }}" hx-target="#dashboard-content"
|
||||
class="px-3 py-1 rounded bg-gray-200 hover:bg-gray-300 text-sm">« Prev</button>
|
||||
{% endif %}
|
||||
|
||||
{% for page_num in pagination.iter_pages(left_edge=1, right_edge=1, left_current=2, right_current=2) %}
|
||||
{% if page_num %}
|
||||
<button hx-get="{{ url_for('main.dashboard_list', page=page_num) }}" hx-target="#dashboard-content"
|
||||
class="px-3 py-1 rounded text-sm {% if page_num == pagination.page %}bg-primary-600 text-white{% else %}bg-gray-200 hover:bg-gray-300{% endif %}">
|
||||
{{ page_num }}
|
||||
</button>
|
||||
{% else %}
|
||||
<span class="text-gray-400">…</span>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
{% if pagination.has_next %}
|
||||
<button hx-get="{{ url_for('main.dashboard_list', page=pagination.next_num) }}" hx-target="#dashboard-content"
|
||||
class="px-3 py-1 rounded bg-gray-200 hover:bg-gray-300 text-sm">Next »</button>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
88
app/templates/partials/dashboard_monthly.html
Normal file
88
app/templates/partials/dashboard_monthly.html
Normal file
@@ -0,0 +1,88 @@
|
||||
<div class="flex flex-col px-2 py-2 -mb-px">
|
||||
<!-- Monthly Navigation -->
|
||||
<div class="flex justify-between items-center mb-4 px-2 mt-2">
|
||||
<button hx-get="{{ url_for('main.dashboard_monthly', month_offset=month_offset - 1) }}"
|
||||
hx-target="#dashboard-content"
|
||||
class="flex items-center text-primary-600 hover:text-primary-800 font-medium transition-colors">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-1" fill="none" viewBox="0 0 24 24"
|
||||
stroke="currentColor" stroke-width="2">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M15 19l-7-7 7-7" />
|
||||
</svg>
|
||||
Previous Month
|
||||
</button>
|
||||
|
||||
<h2 class="text-xl font-bold text-gray-800">{{ target_month_date.strftime('%B %Y') }}</h2>
|
||||
|
||||
<button hx-get="{{ url_for('main.dashboard_monthly', month_offset=month_offset + 1) }}"
|
||||
hx-target="#dashboard-content"
|
||||
class="flex items-center text-primary-600 hover:text-primary-800 font-medium transition-colors {% if month_offset >= 0 %}opacity-50 pointer-events-none{% endif %}">
|
||||
Next Month
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 ml-1" fill="none" viewBox="0 0 24 24"
|
||||
stroke="currentColor" stroke-width="2">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M9 5l7 7-7 7" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-7 pl-2 pr-2">
|
||||
|
||||
<div class="p-2 h-10 text-center font-bold">
|
||||
<span class="xl:block lg:block md:block sm:block hidden">Sunday</span>
|
||||
<span class="xl:hidden lg:hidden md:hidden sm:hidden block">Sun</span>
|
||||
</div>
|
||||
<div class="p-2 h-10 text-center font-bold">
|
||||
<span class="xl:block lg:block md:block sm:block hidden">Monday</span>
|
||||
<span class="xl:hidden lg:hidden md:hidden sm:hidden block">Mon</span>
|
||||
</div>
|
||||
<div class="p-2 h-10 text-center font-bold">
|
||||
<span class="xl:block lg:block md:block sm:block hidden">Tuesday</span>
|
||||
<span class="xl:hidden lg:hidden md:hidden sm:hidden block">Tue</span>
|
||||
</div>
|
||||
<div class="p-2 h-10 text-center font-bold">
|
||||
<span class="xl:block lg:block md:block sm:block hidden">Wednesday</span>
|
||||
<span class="xl:hidden lg:hidden md:hidden sm:hidden block">Wed</span>
|
||||
</div>
|
||||
<div class="p-2 h-10 text-center font-bold">
|
||||
<span class="xl:block lg:block md:block sm:block hidden">Thursday</span>
|
||||
<span class="xl:hidden lg:hidden md:hidden sm:hidden block">Thu</span>
|
||||
</div>
|
||||
<div class="p-2 h-10 text-center font-bold">
|
||||
<span class="xl:block lg:block md:block sm:block hidden">Friday</span>
|
||||
<span class="xl:hidden lg:hidden md:hidden sm:hidden block">Fri</span>
|
||||
</div>
|
||||
<div class="p-2 h-10 text-center font-bold">
|
||||
<span class="xl:block lg:block md:block sm:block hidden">Saturday</span>
|
||||
<span class="xl:hidden lg:hidden md:hidden sm:hidden block">Sat</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-7 overflow-hidden flex-1 pl-2 pr-2 w-full">
|
||||
|
||||
{% for day in month %}
|
||||
<div
|
||||
class="{% if day.is_today %}border-2 border-primary-400 bg-primary-50{% else %}border bg-white{% endif %} flex flex-col min-h-[120px] p-1.5 transition-colors {% if not day.is_in_current_month %}bg-gray-50 opacity-50{% endif %}">
|
||||
<div class="text-right w-full mb-1">
|
||||
<span
|
||||
class="text-xs font-semibold {% if day.is_today %}text-primary-600{% else %}text-gray-500{% endif %}">{{
|
||||
day.day }}</span>
|
||||
</div>
|
||||
|
||||
<div class="space-y-1 flex-grow">
|
||||
{% for reading in day.readings %}
|
||||
<a href="{{ url_for('reading.edit_reading', reading_id=reading.id) }}"
|
||||
class="flex flex-col xl:flex-row justify-between items-center px-1 py-1 bg-white border border-gray-100 rounded shadow-sm hover:border-primary-300 hover:bg-primary-50 transition-colors">
|
||||
<span class="text-[10px] sm:text-xs font-bold text-gray-800 leading-none mb-0.5 xl:mb-0">{{
|
||||
reading.systolic }}/{{ reading.diastolic }}</span>
|
||||
<div class="flex items-center gap-0.5">
|
||||
<span
|
||||
class="text-[9px] sm:text-[10px] font-medium bg-red-50 text-red-600 px-1 rounded leading-none py-0.5"
|
||||
title="Heart Rate">{{ reading.heart_rate }}</span>
|
||||
</div>
|
||||
</a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
56
app/templates/partials/dashboard_weekly.html
Normal file
56
app/templates/partials/dashboard_weekly.html
Normal file
@@ -0,0 +1,56 @@
|
||||
<div>
|
||||
<!-- Weekly Navigation -->
|
||||
<div class="flex justify-between items-center mb-4 px-2">
|
||||
<button hx-get="{{ url_for('main.dashboard_weekly', week_offset=week_offset - 1) }}"
|
||||
hx-target="#dashboard-content"
|
||||
class="flex items-center text-primary-600 hover:text-primary-800 font-medium transition-colors">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-1" fill="none" viewBox="0 0 24 24"
|
||||
stroke="currentColor" stroke-width="2">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M15 19l-7-7 7-7" />
|
||||
</svg>
|
||||
Previous Week
|
||||
</button>
|
||||
|
||||
<span class="text-gray-600 font-semibold {% if week_offset == 0 %}text-primary-600{% endif %}">
|
||||
{% if week_offset == 0 %}This Week{% elif week_offset == -1 %}Last Week{% elif week_offset == 1 %}Next
|
||||
Week{% else %}{{ week_offset|abs }} weeks {% if week_offset < 0 %}ago{% else %}from now{% endif %}{% endif
|
||||
%} </span>
|
||||
|
||||
<button hx-get="{{ url_for('main.dashboard_weekly', week_offset=week_offset + 1) }}"
|
||||
hx-target="#dashboard-content"
|
||||
class="flex items-center text-primary-600 hover:text-primary-800 font-medium transition-colors {% if week_offset >= 0 %}opacity-50 pointer-events-none{% endif %}">
|
||||
Next Week
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 ml-1" fill="none" viewBox="0 0 24 24"
|
||||
stroke="currentColor" stroke-width="2">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M9 5l7 7-7 7" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-7 text-center">
|
||||
{% for day in week %}
|
||||
<div class="border p-2 bg-gray-50 flex flex-col min-h-[140px]">
|
||||
<div class="text-sm font-bold text-gray-500 mb-2">{{ day.date }}</div>
|
||||
{% if day.readings %}
|
||||
<div class="space-y-1.5 flex-grow">
|
||||
{% for reading in day.readings %}
|
||||
<a href="{{ url_for('reading.edit_reading', reading_id=reading.id) }}"
|
||||
class="flex flex-col 2xl:flex-row justify-between items-center px-1.5 py-1 bg-white border border-gray-200 rounded shadow-sm hover:border-primary-400 hover:bg-primary-50 transition-colors">
|
||||
<span class="text-xs font-bold text-gray-800">{{ reading.systolic }}/{{ reading.diastolic
|
||||
}}</span>
|
||||
<div class="flex items-center gap-1 2xl:mt-0 mt-0.5">
|
||||
<span class="text-[10px] font-medium bg-red-50 text-red-600 px-1 rounded" title="Heart Rate">{{
|
||||
reading.heart_rate }}</span>
|
||||
<span class="text-[10px] text-gray-400 font-medium whitespace-nowrap">{{
|
||||
reading.local_timestamp.strftime('%H:%M') }}</span>
|
||||
</div>
|
||||
</a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="flex-grow"></div> <!-- Spacer -->
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
Reference in New Issue
Block a user