Change look of users list of workouts, added month calendar (static for now), & and added in hyperscript (will switch to using this for client side stuff)

This commit is contained in:
Peter Stockings
2023-10-13 15:33:14 +11:00
parent c18beba563
commit 861d83f828
5 changed files with 336 additions and 50 deletions

2
app.py
View File

@@ -10,6 +10,7 @@ import matplotlib.dates as mdates
import os
import sparklines
from dateutil.parser import isoparse
import humanize
app = Flask(__name__)
@@ -378,6 +379,7 @@ def get_workouts_for_user(user_id):
'user_name': user.name,
'start_time': format_date_with_ordinal(start_time, '%#H:%M %B %dth %Y'),
'start_time_date': start_time,
'start_time_ago': humanize.naturaltime(start_time),
'duration': format_duration(duration),
'duration_minutes': duration.total_seconds() / 60,
'average_rpm': int(average_rpm),

View File

@@ -8,4 +8,5 @@ python-dateutil==2.8.2
bidict==0.22.1
Flask-SQLAlchemy==3.0.3
matplotlib==3.5.2
sparklines==0.4.2
sparklines==0.4.2
humanize==4.8.0

View File

@@ -0,0 +1,265 @@
<div class="md:p-8 p-5 dark:bg-gray-800 bg-white rounded-t">
<div class="px-4 flex items-center justify-between">
<span tabindex="0" class="focus:outline-none text-base font-bold dark:text-gray-100 text-gray-800">October
2020</span>
<div class="flex items-center">
<button aria-label="calendar backward"
class="focus:text-gray-400 hover:text-gray-400 text-gray-800 dark:text-gray-100">
<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-chevron-left" width="24"
height="24" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none"
stroke-linecap="round" stroke-linejoin="round" data-darkreader-inline-stroke=""
style="--darkreader-inline-stroke: currentColor;">
<path stroke="none" d="M0 0h24v24H0z" fill="none" data-darkreader-inline-stroke=""
style="--darkreader-inline-stroke: none;"></path>
<polyline points="15 6 9 12 15 18"></polyline>
</svg>
</button>
<button aria-label="calendar forward"
class="focus:text-gray-400 hover:text-gray-400 ml-3 text-gray-800 dark:text-gray-100">
<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-chevron-right" width="24"
height="24" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none"
stroke-linecap="round" stroke-linejoin="round" data-darkreader-inline-stroke=""
style="--darkreader-inline-stroke: currentColor;">
<path stroke="none" d="M0 0h24v24H0z" fill="none" data-darkreader-inline-stroke=""
style="--darkreader-inline-stroke: none;"></path>
<polyline points="9 6 15 12 9 18"></polyline>
</svg>
</button>
</div>
</div>
<div class="flex items-center justify-between pt-12 overflow-x-auto">
<table class="w-full">
<thead>
<tr>
<th>
<div class="w-full flex justify-center">
<p class="text-base font-medium text-center text-gray-800 dark:text-gray-100">Mo</p>
</div>
</th>
<th>
<div class="w-full flex justify-center">
<p class="text-base font-medium text-center text-gray-800 dark:text-gray-100">Tu</p>
</div>
</th>
<th>
<div class="w-full flex justify-center">
<p class="text-base font-medium text-center text-gray-800 dark:text-gray-100">We</p>
</div>
</th>
<th>
<div class="w-full flex justify-center">
<p class="text-base font-medium text-center text-gray-800 dark:text-gray-100">Th</p>
</div>
</th>
<th>
<div class="w-full flex justify-center">
<p class="text-base font-medium text-center text-gray-800 dark:text-gray-100">Fr</p>
</div>
</th>
<th>
<div class="w-full flex justify-center">
<p class="text-base font-medium text-center text-gray-800 dark:text-gray-100">Sa</p>
</div>
</th>
<th>
<div class="w-full flex justify-center">
<p class="text-base font-medium text-center text-gray-800 dark:text-gray-100">Su</p>
</div>
</th>
</tr>
</thead>
<tbody>
<tr>
<td class="pt-6">
<div class="px-2 py-2 cursor-pointer flex w-full justify-center">
<p class="text-base text-gray-300 dark:text-gray-100 font-medium">29</p>
</div>
</td>
<td class="pt-6">
<div class="px-2 py-2 cursor-pointer flex w-full justify-center">
<p class="text-base text-gray-300 dark:text-gray-100 font-medium">30</p>
</div>
</td>
<td class="pt-6">
<div class="px-2 py-2 cursor-pointer flex w-full justify-center">
<p class="text-base text-gray-300 dark:text-gray-100 font-medium">31</p>
</div>
</td>
<td class="pt-6">
<div class="px-2 py-2 cursor-pointer flex w-full justify-center">
<p class="text-base text-gray-500 dark:text-gray-100 font-medium">1</p>
</div>
</td>
<td class="pt-6">
<div class="px-2 py-2 cursor-pointer flex w-full justify-center">
<p class="text-base text-gray-500 dark:text-gray-100 font-medium">2</p>
</div>
</td>
<td class="pt-6">
<div class="px-2 py-2 cursor-pointer flex w-full justify-center">
<p class="text-base text-gray-500 dark:text-gray-100">3</p>
</div>
</td>
<td class="pt-6">
<div class="px-2 py-2 cursor-pointer flex w-full justify-center">
<p class="text-base text-gray-500 dark:text-gray-100">4</p>
</div>
</td>
</tr>
<tr>
<td>
<div class="px-2 py-2 cursor-pointer flex w-full justify-center">
<p class="text-base text-gray-500 dark:text-gray-100 font-medium">5</p>
</div>
</td>
<td>
<div class="px-2 py-2 cursor-pointer flex w-full justify-center">
<p class="text-base text-gray-500 dark:text-gray-100 font-medium">6</p>
</div>
</td>
<td>
<div class="px-2 py-2 cursor-pointer flex w-full justify-center">
<a role="link" tabindex="0"
class="focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-700 focus:bg-indigo-200 hover:bg-indigo-200 text-base w-8 h-8 flex items-center justify-center font-medium border border-indigo-700 rounded-full">7</a>
</div>
</td>
<td>
<div class="w-full h-full">
<div class="flex items-center justify-center w-full rounded-full cursor-pointer">
<a role="link" tabindex="0"
class="focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-700 focus:bg-indigo-500 hover:bg-indigo-500 text-base w-8 h-8 flex items-center justify-center font-medium text-white bg-indigo-700 rounded-full">8</a>
</div>
</div>
</td>
<td>
<div class="px-2 py-2 cursor-pointer flex w-full justify-center">
<p class="text-base text-gray-500 dark:text-gray-100 font-medium">9</p>
</div>
</td>
<td>
<div class="px-2 py-2 cursor-pointer flex w-full justify-center">
<p class="text-base text-gray-500 dark:text-gray-100">10</p>
</div>
</td>
<td>
<div class="px-2 py-2 cursor-pointer flex w-full justify-center">
<p class="text-base text-gray-500 dark:text-gray-100">11</p>
</div>
</td>
</tr>
<tr>
<td>
<div class="px-2 py-2 cursor-pointer flex w-full justify-center">
<p class="text-base text-gray-500 dark:text-gray-100 font-medium">12</p>
</div>
</td>
<td>
<div class="px-2 py-2 cursor-pointer flex w-full justify-center">
<p class="text-base text-gray-500 dark:text-gray-100 font-medium">13</p>
</div>
</td>
<td>
<div class="px-2 py-2 cursor-pointer flex w-full justify-center">
<p class="text-base text-gray-500 dark:text-gray-100 font-medium">14</p>
</div>
</td>
<td>
<div class="px-2 py-2 cursor-pointer flex w-full justify-center">
<p class="text-base text-gray-500 dark:text-gray-100 font-medium">15</p>
</div>
</td>
<td>
<div class="px-2 py-2 cursor-pointer flex w-full justify-center">
<p class="text-base text-gray-500 dark:text-gray-100 font-medium">16</p>
</div>
</td>
<td>
<div class="px-2 py-2 cursor-pointer flex w-full justify-center">
<p class="text-base text-gray-500 dark:text-gray-100">17</p>
</div>
</td>
<td>
<div class="px-2 py-2 cursor-pointer flex w-full justify-center">
<p class="text-base text-gray-500 dark:text-gray-100">18</p>
</div>
</td>
</tr>
<tr>
<td>
<div class="px-2 py-2 cursor-pointer flex w-full justify-center">
<p class="text-base text-gray-500 dark:text-gray-100 font-medium">19</p>
</div>
</td>
<td>
<div class="px-2 py-2 cursor-pointer flex w-full justify-center">
<p class="text-base text-gray-500 dark:text-gray-100 font-medium">20</p>
</div>
</td>
<td>
<div class="px-2 py-2 cursor-pointer flex w-full justify-center">
<p class="text-base text-gray-500 dark:text-gray-100 font-medium">21</p>
</div>
</td>
<td>
<div class="px-2 py-2 cursor-pointer flex w-full justify-center">
<p class="text-base text-gray-500 dark:text-gray-100 font-medium">22</p>
</div>
</td>
<td>
<div class="px-2 py-2 cursor-pointer flex w-full justify-center">
<p class="text-base text-gray-500 dark:text-gray-100 font-medium">23</p>
</div>
</td>
<td>
<div class="px-2 py-2 cursor-pointer flex w-full justify-center">
<p class="text-base text-gray-500 dark:text-gray-100">24</p>
</div>
</td>
<td>
<div class="px-2 py-2 cursor-pointer flex w-full justify-center">
<p class="text-base text-gray-500 dark:text-gray-100">25</p>
</div>
</td>
</tr>
<tr>
<td>
<div class="px-2 py-2 cursor-pointer flex w-full justify-center">
<p class="text-base text-gray-500 dark:text-gray-100 font-medium">26</p>
</div>
</td>
<td>
<div class="px-2 py-2 cursor-pointer flex w-full justify-center">
<p class="text-base text-gray-500 dark:text-gray-100 font-medium">27</p>
</div>
</td>
<td>
<div class="px-2 py-2 cursor-pointer flex w-full justify-center">
<p class="text-base text-gray-500 dark:text-gray-100 font-medium">28</p>
</div>
</td>
<td>
<div class="px-2 py-2 cursor-pointer flex w-full justify-center">
<p class="text-base text-gray-500 dark:text-gray-100 font-medium">29</p>
</div>
</td>
<td>
<div class="px-2 py-2 cursor-pointer flex w-full justify-center">
<p class="text-base text-gray-500 dark:text-gray-100 font-medium">30</p>
</div>
</td>
<td>
<div class="px-2 py-2 cursor-pointer flex w-full justify-center">
<p class="text-base text-gray-300 dark:text-gray-100 font-medium">1</p>
</div>
</td>
<td>
<div class="px-2 py-2 cursor-pointer flex w-full justify-center">
<p class="text-base text-gray-300 dark:text-gray-100 font-medium">2</p>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>

View File

@@ -25,6 +25,7 @@
};
</script>
<script src="/static/js/htmx.min.js"></script>
<script src="/static/js/hyperscript.min.js"></script>
</head>
<body class="bg-gray-100 font-sans leading-normal tracking-normal">

View File

@@ -63,61 +63,78 @@
</button>
</h2>
<div class="!visible collapse p-4 hidden">
<div>
{% for w in workouts %}
<div class="border-b-2 border-neutral-200 bg-white dark:border-neutral-600 dark:bg-neutral-800">
<h2 class="mb-0">
<button
class="accordion-button group relative flex w-full items-center rounded-t-[15px] border-0 bg-white py-4 px-5 text-left text-base text-neutral-800 transition [overflow-anchor:none] hover:z-[2] focus:z-[3] focus:outline-none dark:bg-neutral-800 dark:text-white [&:not([data-te-collapse-collapsed])]:bg-white [&:not([data-te-collapse-collapsed])]:text-primary [&:not([data-te-collapse-collapsed])]:[box-shadow:inset_0_-1px_0_rgba(229,231,235)] dark:[&:not([data-te-collapse-collapsed])]:bg-neutral-800 dark:[&:not([data-te-collapse-collapsed])]:text-primary-400 dark:[&:not([data-te-collapse-collapsed])]:[box-shadow:inset_0_-1px_0_rgba(75,85,99)]"
type="button">
<div class="w-full">
<div class="flex flex-row justify-between">
<h2 class="text-lg font-medium text-gray-900">{{ w.start_time }}
</h2>
<h2 class="text-md font-small text-gray-600 pr-8">{{ w.bike_display_name }}
</h2>
</div>
{{ render_partial('partials/calendar.html', foo=1) }}
<p class="text-sm text-gray-500">Duration: {{ w.duration }} | Average RPM: {{ w.average_rpm
}} |
Calories: {{ w.calories }} | Distance: {{ w.distance }} {% if w.is_heart_rate_available
%} | Average: BPM: {{w.average_bpm }} {% endif %}</p>
</div>
<span
class="ml-auto h-5 w-5 shrink-0 rotate-[-180deg] fill-[#336dec] transition-transform duration-200 ease-in-out group-[[data-te-collapse-collapsed]]:rotate-0 group-[[data-te-collapse-collapsed]]:fill-[#212529] motion-reduce:transition-none dark:fill-blue-300 dark:group-[[data-te-collapse-collapsed]]:fill-white">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5"
stroke="currentColor" class="h-6 w-6">
<path stroke-linecap="round" stroke-linejoin="round" d="M19.5 8.25l-7.5 7.5-7.5-7.5" />
</svg>
</span>
</button>
</h2>
<div class="!visible collapse {% if loop.index != 1 %}hidden{% endif %}">
<div id="workout_view-{{ w.id }}">
{% with workout=w, graph_types=w['selected_graph_types'] %}
{% include 'workout_view.html' %}
{% endwith %}
<ol class="relative border-l border-gray-200 dark:border-gray-700">
{% for w in workouts[:5] %}
<li class="mb-10 ml-6">
<span
class="absolute flex items-center justify-center w-6 h-6 bg-blue-100 rounded-full -left-3 ring-8 ring-white dark:ring-gray-900 dark:bg-blue-900">
<svg class="w-2.5 h-2.5 text-blue-800 dark:text-blue-300" aria-hidden="true"
xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 20 20"
data-darkreader-inline-fill="" style="--darkreader-inline-fill: currentColor;">
<path
d="M20 4a2 2 0 0 0-2-2h-2V1a1 1 0 0 0-2 0v1h-3V1a1 1 0 0 0-2 0v1H6V1a1 1 0 0 0-2 0v1H2a2 2 0 0 0-2 2v2h20V4ZM0 18a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V8H0v10Zm5-8h10a1 1 0 0 1 0 2H5a1 1 0 0 1 0-2Z">
</path>
</svg>
</span>
<div
class="p-4 bg-white border border-gray-200 rounded-lg shadow-sm dark:bg-gray-700 dark:border-gray-600 cursor-pointer">
<div class="items-center justify-between sm:flex">
<time class="mb-1 text-xs font-normal text-gray-400 sm:order-last sm:mb-0">{{ w.start_time_ago
}}</time>
<div class="text-sm font-normal text-gray-500 lex dark:text-gray-300"
_="on click toggle .hidden on #workout_view_wrapper-{{ w.id }}">Duration: {{ w.duration }}
|
Average RPM: {{ w.average_rpm
}} |
Calories: {{ w.calories }} | Distance: {{ w.distance }} {% if w.is_heart_rate_available
%} | Average: BPM: {{w.average_bpm }} {% endif %}<span
class="bg-gray-100 text-gray-800 text-xs font-normal mr-2 px-2.5 py-0.5 rounded dark:bg-gray-600 dark:text-gray-300">{{
w.bike_display_name }}</span> </div>
</div>
<button
class="mx-4 mb-4 bg-gray-300 hover:bg-gray-400 text-gray-800 font-bold py-2 px-4 rounded inline-flex items-center"
hx-delete="{{ url_for('delete_workout', user_id=w.user_id, workout_id=w.id) }}"
hx-confirm="Are you sure you wish to delete this {{ w.duration }} workout at {{ w.start_time }}"
hx-target="#container">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5"
stroke="currentColor" class="w-6 h-6">
<path stroke-linecap="round" stroke-linejoin="round"
d="M14.74 9l-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 01-2.244 2.077H8.084a2.25 2.25 0 01-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 00-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 013.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 00-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 00-7.5 0" />
</svg>
<span>Delete</span>
</button>
<div class="p-3 text-xs italic font-normal text-gray-500 border border-gray-200 rounded-lg bg-gray-50 dark:bg-gray-600 dark:border-gray-500 dark:text-gray-300 mt-3 hidden"
id="workout_view_wrapper-{{ w.id }}">
<div id="workout_view-{{ w.id }}">
{% with workout=w, graph_types=w['selected_graph_types'] %}
{% include 'workout_view.html' %}
{% endwith %}
</div>
<button
class="mx-4 mb-4 bg-gray-300 hover:bg-gray-400 text-gray-800 font-bold py-2 px-4 rounded inline-flex items-center"
hx-delete="{{ url_for('delete_workout', user_id=w.user_id, workout_id=w.id) }}"
hx-confirm="Are you sure you wish to delete this {{ w.duration }} workout at {{ w.start_time }}"
hx-target="#container">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5"
stroke="currentColor" class="w-6 h-6">
<path stroke-linecap="round" stroke-linejoin="round"
d="M14.74 9l-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 01-2.244 2.077H8.084a2.25 2.25 0 01-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 00-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 013.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 00-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 00-7.5 0" />
</svg>
<span>Delete</span>
</button>
</div>
</div>
</div>
{% endfor %}
</div>
</li>
{% endfor%}
</ol>
<div class="flex justify-center">
<button type="button"
class="py-2.5 px-5 mr-2 mb-2 text-sm font-medium text-gray-900 focus:outline-none bg-white rounded-lg border border-gray-200 hover:bg-gray-100 hover:text-blue-700 focus:z-10 focus:ring-4 focus:ring-gray-200 dark:focus:ring-gray-700 dark:bg-gray-800 dark:text-gray-400 dark:border-gray-600 dark:hover:text-white dark:hover:bg-gray-700">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5"
stroke="currentColor" class="w-6 h-6">
<path stroke-linecap="round" stroke-linejoin="round"
d="M6.75 12a.75.75 0 11-1.5 0 .75.75 0 011.5 0zM12.75 12a.75.75 0 11-1.5 0 .75.75 0 011.5 0zM18.75 12a.75.75 0 11-1.5 0 .75.75 0 011.5 0z" />
</svg>
</button>
</div>
</div>
{% endif %}
</div>