Remove wrappers for calendar, dashboard, & person
This commit is contained in:
@@ -2,8 +2,190 @@
|
||||
|
||||
{% block content %}
|
||||
|
||||
{{ render_partial('partials/page/calendar.html',
|
||||
person=person, selected_date=selected_date, selected_view=selected_view, next_date=next_date,
|
||||
previous_date=previous_date, start_date=start_date, end_date=end_date) }}
|
||||
<a hx-get="{{ url_for('get_calendar', person_id=person['PersonId']) }}" hx-target="#container"
|
||||
hx-vals='{"date": "{{ selected_date }}", "view": "{{ selected_view }}"}' hx-trigger="refreshView"
|
||||
id="refreshViewElement"></a>
|
||||
|
||||
<div class="flex flex-grow flex-col bg-white sm:rounded shadow overflow-hidden">
|
||||
<div class="flex items-center justify-between pt-2 pb-2">
|
||||
<div class="flex">
|
||||
<div class="flex ml-6">
|
||||
<button hx-get="{{ url_for('get_calendar', person_id=person['PersonId']) }}" hx-target="#container"
|
||||
hx-vals='{"date": "{{ previous_date }}"}' hx-include="[name='view']" hx-push-url="true">
|
||||
<svg class="w-6 h-6" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
|
||||
stroke="currentColor" data-darkreader-inline-stroke=""
|
||||
style="--darkreader-inline-stroke:currentColor;">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7">
|
||||
</path>
|
||||
</svg>
|
||||
</button>
|
||||
<button hx-get="{{ url_for('get_calendar', person_id=person['PersonId']) }}" hx-target="#container"
|
||||
hx-vals='{"date": "{{ next_date }}"}' hx-include="[name='view']" hx-push-url="true">
|
||||
<svg class="w-6 h-6" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
|
||||
stroke="currentColor" data-darkreader-inline-stroke=""
|
||||
style="--darkreader-inline-stroke:currentColor;">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7">
|
||||
</path>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{% if selected_view == 'month' %}
|
||||
<h2 class="ml-2 text-xl font-bold leading-none">{{ selected_date | strftime('%B, %Y') }}</h2>
|
||||
{% else %}
|
||||
<h2 class="ml-2 text-xl font-bold leading-none">{{ selected_date | strftime('%Y') }}</h2>
|
||||
{% endif %}
|
||||
<span
|
||||
class="bg-blue-100 text-blue-800 text-sm font-medium mr-2 px-2.5 py-0.5 rounded dark:bg-blue-200 dark:text-blue-800 ml-5">{{
|
||||
person['PersonName']}}</span>
|
||||
</div>
|
||||
|
||||
<div class="mr-4">
|
||||
<select id="workout-view-picker" data-te-select-init data-te-select-filter="true" data-te-select-size="lg"
|
||||
name="view" hx-get="{{ url_for('get_calendar', person_id=person['PersonId']) }}" hx-target="#container"
|
||||
hx-vals='{"date": "{{ selected_date }}"}' hx-push-url="true">
|
||||
<option value="month" {% if selected_view=='month' %}selected{% endif %}>Month</option>
|
||||
<option value="year" {% if selected_view=='year' %}selected{% endif %}>Year</option>
|
||||
<option value="all">All</option>
|
||||
</select>
|
||||
<script>
|
||||
te.Select.getOrCreateInstance(document.querySelector("#workout-view-picker")).setValue("{{ selected_view | safe }}");
|
||||
</script>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if selected_view == 'month' %}
|
||||
<div class="flex flex-col px-2 py-2 -mb-px">
|
||||
<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 i in range((end_date-start_date).days + 1) %}
|
||||
{% set date = start_date + timedelta(days=i) %}
|
||||
{% set workout = person['Workouts'] |
|
||||
get_first_element_from_list_with_matching_attribute(
|
||||
'StartDate',
|
||||
date) %}
|
||||
<div class="{% if date == datetime.today().date() %}rounded-md border-4 border-green-50{% endif %} border flex flex-col h-50 sm:h-40 md:h-30 lg:h-30 mx-auto mx-auto overflow-hidden w-full pt-2 pl-1 cursor-pointer {% if selected_date.month != date.month %}bg-gray-100{% endif %}"
|
||||
{% if workout %}
|
||||
hx-get="{{ url_for('get_workout_modal', person_id=person['PersonId'], workout_id=workout['WorkoutId']) }}"
|
||||
hx-target='body' hx-swap='beforeend' {% endif %}>
|
||||
<div class="top h-5 w-full">
|
||||
<span class="text-gray-500 font-semibold">{{ date.day }}</span>
|
||||
</div>
|
||||
<div class="bottom flex-grow py-1 w-full">
|
||||
{% if workout['TopSets']|length > 0 %}
|
||||
{% for topset in workout['TopSets'] %}
|
||||
<button
|
||||
class="flex flex-col xl:flex-row items-start lg:items-center flex-shrink-0 px-0 sm:px-0.5 md:px-0.5 lg:px-0.5 text-xs">
|
||||
<span class="ml-0 sm:ml-0.5 md:ml-2 lg:ml-2 font-medium leading-none truncate">{{
|
||||
topset['ExerciseName'] }}</span>
|
||||
<span class="ml-0 sm:ml-0.5 md:ml-2 lg:ml-2 font-light leading-none">{{ topset['Repetitions'] }}
|
||||
x
|
||||
{{
|
||||
topset['Weight']
|
||||
}}kg</span>
|
||||
</button>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
{% elif selected_view == 'year'%}
|
||||
<div class="flex justify-center px-2 py-2 -mb-px">
|
||||
|
||||
<div
|
||||
class="grid grid-cols-1 lg:grid-cols-2 xl:grid-cols-3 2xl:grid-cols-4 gap-4 lg:gap-10 bg-white overflow-hidden">
|
||||
{% for i in range(12) %}
|
||||
{% set date = start_date + relativedelta(months=i) %}
|
||||
{% set first_day_of_month = date.replace(day=1) %}
|
||||
{% set last_day_of_month = date + relativedelta(day=31) %}
|
||||
{% set (first_day, last_day) = first_and_last_visible_days_in_month(first_day_of_month, last_day_of_month)
|
||||
%}
|
||||
<div>
|
||||
<div class="bg-grey-lighter font-semibold text-center cursor-pointer"
|
||||
hx-get="{{ url_for('get_calendar', person_id=person['PersonId']) }}" hx-target="#container"
|
||||
hx-vals='{"date": "{{ first_day_of_month }}", "view": "month"}' hx-push-url="true">{{
|
||||
first_day_of_month | strftime('%B %Y') }}
|
||||
</div>
|
||||
<div>
|
||||
<div class="grid grid-cols-7 border-b font-semibold">
|
||||
<div class="py-3 px-4">S</div>
|
||||
<div class="py-3 px-4">M</div>
|
||||
<div class="py-3 px-4">T</div>
|
||||
<div class="py-3 px-4">W</div>
|
||||
<div class="py-3 px-4">T</div>
|
||||
<div class="py-3 px-4">F</div>
|
||||
<div class="py-3 px-4">S</div>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-7 overflow-hidden flex-1 pl-2 pr-2 w-full">
|
||||
{% for i in range((last_day-first_day).days + 1) %}
|
||||
{% set day_date = first_day + timedelta(days=i) %}
|
||||
{% set workout = person['Workouts'] | get_first_element_from_list_with_matching_attribute(
|
||||
'StartDate',
|
||||
day_date) %}
|
||||
{% set is_in_month = day_date.month == first_day_of_month.month%}
|
||||
<div class="{% if day_date == datetime.today().date() and is_in_month %}rounded-md border-4 border-green-50 border{% endif %} py-3 px-4 hover:bg-blue hover:text-white text-center cursor-pointer rounded-md {% if workout and is_in_month %}bg-green-100{% endif %}"
|
||||
{% if workout %}
|
||||
hx-get="{{ url_for('get_workout_modal', person_id=person['PersonId'], workout_id=workout['WorkoutId']) }}"
|
||||
hx-target='body' hx-swap='beforeend' {% endif %}>
|
||||
{% if is_in_month %}
|
||||
{{ day_date.day }} {% endif %}</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{{ render_partial('partials/stats.html', stats=person['Stats']) }}
|
||||
|
||||
<button
|
||||
class="fixed z-90 bottom-10 right-8 bg-blue-600 w-20 h-20 rounded-full drop-shadow-lg flex justify-center items-center text-white text-4xl hover:bg-blue-700 hover:drop-shadow-2xl hover:animate-bounce duration-300"
|
||||
hx-post="{{ url_for('create_workout', person_id=person['PersonId']) }}" hx-target='body' hx-swap='beforeend'>
|
||||
<svg viewBox="0 0 20 20" enable-background="new 0 0 20 20" class="w-6 h-6 inline-block">
|
||||
<path fill="#FFFFFF" d="M16,10c0,0.553-0.048,1-0.601,1H11v4.399C11,15.951,10.553,16,10,16c-0.553,0-1-0.049-1-0.601V11H4.601
|
||||
C4.049,11,4,10.553,4,10c0-0.553,0.049-1,0.601-1H9V4.601C9,4.048,9.447,4,10,4c0.553,0,1,0.048,1,0.601V9h4.399
|
||||
C15.952,9,16,9.447,16,10z" />
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
{% endblock %}
|
||||
@@ -2,8 +2,181 @@
|
||||
|
||||
{% block content %}
|
||||
|
||||
{{ render_partial('partials/page/dashboard.html',
|
||||
model=model, people=people, exercises=exercises, min_date=min_date, max_date=max_date,
|
||||
selected_person_ids=selected_person_ids, selected_exercise_ids=selected_exercise_ids, tags=tags) }}
|
||||
<div class="bg-white shadow rounded-lg p-4 w-full mb-4">
|
||||
<div class="flex flex-wrap">
|
||||
<div class="w-full lg:w-1/4 sm:w-full px-3 mb-6 md:mb-0">
|
||||
<div class="flex flex-wrap -mx-3">
|
||||
<div class="w-full px-3">
|
||||
<div class="mb-3 w-full"><label
|
||||
class="block uppercase tracking-wide text-gray-700 text-xs font-bold mb-2"
|
||||
for="grid-city">People</label>
|
||||
<select class="bg-gray-50 border border-gray-300 hidden" data-te-select-filter="true"
|
||||
data-te-select-init="" data-te-select-size="lg" hx-get="{{ url_for('dashboard') }}"
|
||||
hx-include="[name='min_date'],[name='max_date'],[name='exercise_id']" hx-push-url="true"
|
||||
hx-target="#container" id="dashboard-people-multi-select" multiple="" name="person_id">
|
||||
{% for p in people %}
|
||||
<option value="{{ p['PersonId'] }}">{{
|
||||
p['Name']
|
||||
}}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<script>te.Select.getOrCreateInstance(document.querySelector("#dashboard-people-multi-select")).setValue({{ selected_person_ids | list_to_string | safe}})</script>
|
||||
</div>
|
||||
</div>
|
||||
<div class="w-full lg:w-1/4 sm:w-full px-3 mb-6 md:mb-0">
|
||||
<div class="flex flex-wrap -mx-3">
|
||||
<div class="w-full px-3">
|
||||
<div class="mb-3 w-full"><label
|
||||
class="block uppercase tracking-wide text-gray-700 text-xs font-bold mb-2"
|
||||
for="grid-city">Exercises</label>
|
||||
<select class="bg-gray-50 border border-gray-300 hidden" data-te-select-filter="true"
|
||||
data-te-select-init="" data-te-select-size="lg" hx-get="{{ url_for('dashboard') }}"
|
||||
hx-include="[name='min_date'],[name='max_date'],[name='person_id']" hx-push-url="true"
|
||||
hx-target="#container" id="dashboard-exercise-multi-select" multiple="" name="exercise_id">
|
||||
{% for e in exercises %}
|
||||
<option value="{{ e['ExerciseId'] }}">{{
|
||||
e['Name']
|
||||
}}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<script>te.Select.getOrCreateInstance(document.querySelector("#dashboard-exercise-multi-select")).setValue({{ selected_exercise_ids | list_to_string | safe}})</script>
|
||||
</div>
|
||||
</div>
|
||||
<div class="w-full lg:w-1/4 sm:w-full px-3 mb-6 md:mb-0"><label
|
||||
class="block uppercase tracking-wide text-gray-700 text-xs font-bold mb-2" for="grid-city">Min
|
||||
date</label>
|
||||
<div class="relative">
|
||||
<div class="flex absolute inset-y-0 left-0 items-center pl-3 pointer-events-none"><svg
|
||||
class="w-5 h-5 text-gray-500 dark:text-gray-400" viewBox="0 0 20 20" aria-hidden="true"
|
||||
fill="currentColor" xmlns="http://www.w3.org/2000/svg" data-darkreader-inline-fill=""
|
||||
style="--darkreader-inline-fill:currentColor;">
|
||||
<path
|
||||
d="M6 2a1 1 0 00-1 1v1H4a2 2 0 00-2 2v10a2 2 0 002 2h12a2 2 0 002-2V6a2 2 0 00-2-2h-1V3a1 1 0 10-2 0v1H7V3a1 1 0 00-1-1zm0 5a1 1 0 000 2h8a1 1 0 100-2H6z"
|
||||
clip-rule="evenodd" fill-rule="evenodd"></path>
|
||||
</svg></div><input
|
||||
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full pl-10 p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500 w-full"
|
||||
hx-get="{{ url_for('dashboard') }}"
|
||||
hx-include="[name='min_date'],[name='max_date'],[name='person_id'],[name='exercise_id']"
|
||||
hx-push-url="true" hx-target="#container" hx-trigger="change" name="min_date" type="date"
|
||||
value="{{ min_date }}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="w-full lg:w-1/4 sm:w-full px-3 mb-6 md:mb-0"><label
|
||||
class="block uppercase tracking-wide text-gray-700 text-xs font-bold mb-2" for="grid-zip">Max
|
||||
date</label>
|
||||
<div class="relative">
|
||||
<div class="flex absolute inset-y-0 left-0 items-center pl-3 pointer-events-none"><svg
|
||||
class="w-5 h-5 text-gray-500 dark:text-gray-400" viewBox="0 0 20 20" aria-hidden="true"
|
||||
fill="currentColor" xmlns="http://www.w3.org/2000/svg" data-darkreader-inline-fill=""
|
||||
style="--darkreader-inline-fill:currentColor;">
|
||||
<path
|
||||
d="M6 2a1 1 0 00-1 1v1H4a2 2 0 00-2 2v10a2 2 0 002 2h12a2 2 0 002-2V6a2 2 0 00-2-2h-1V3a1 1 0 10-2 0v1H7V3a1 1 0 00-1-1zm0 5a1 1 0 000 2h8a1 1 0 100-2H6z"
|
||||
clip-rule="evenodd" fill-rule="evenodd"></path>
|
||||
</svg></div><input
|
||||
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full pl-10 p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500 w-full"
|
||||
hx-get="{{ url_for('dashboard') }}" hx-push-url="true" hx-target="#container" hx-trigger="change"
|
||||
name="max_date" type="date" value="{{ max_date }}">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% with person_id=None, tags=tags %}
|
||||
{% include 'partials/tags.html' %}
|
||||
{% endwith %}
|
||||
</div>
|
||||
|
||||
|
||||
{% if model['People']|length == 0 %}
|
||||
<div class="bg-purple-100 rounded-lg py-5 px-6 mb-4 text-base text-purple-700 mb-3" role="alert" id="no-workouts">
|
||||
No workouts selected.
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="w-full grid grid-cols-1 xl:grid-cols-3 2xl:grid-cols-3 gap-4">
|
||||
|
||||
{% for p in model['People'] %}
|
||||
<div class="bg-white shadow rounded-lg p-4 sm:p-6 xl:p-8 ">
|
||||
|
||||
<div class="mb-4 flex items-center justify-between">
|
||||
<div>
|
||||
<h3 class="text-xl font-bold text-gray-900 mb-2">{{ p['PersonName'] }}</h3>
|
||||
<span class="text-base font-normal text-gray-500">Current rep maxes</span>
|
||||
</div>
|
||||
<div class="flex-shrink-0">
|
||||
<a hx-get="{{ url_for('get_calendar' ,person_id=p['PersonId']) }}" hx-push-url="true"
|
||||
hx-target="#container"
|
||||
class="text-sm font-medium text-cyan-600 hover:bg-gray-100 rounded-lg p-2 cursor-pointer">View
|
||||
workouts</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if p['NumberOfWorkouts'] == 0 %}
|
||||
<div class="bg-purple-100 rounded-lg py-5 px-6 mb-4 text-base text-purple-700 mb-3" role="alert">
|
||||
No workouts completed.
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% for e in p['Exercises'] %}
|
||||
<div class="flex flex-col mt-8">
|
||||
<div class="overflow-x-auto rounded-lg">
|
||||
<div class="align-middle inline-block min-w-full">
|
||||
<div class="shadow overflow-hidden sm:rounded-lg">
|
||||
<h4 class="text-l font-semibold text-blue-400 mb-2 text-center">{{ e['ExerciseName'] }}</h4>
|
||||
{% if e['RepMaxes']|length > 1 %}
|
||||
<div id="person-{{ p['PersonId'] }}-exercise-{{ e['ExerciseId'] }}"
|
||||
class="w-full mt-2 aspect-video"></div>
|
||||
<script>
|
||||
Plotly.newPlot(document.getElementById("person-{{ p['PersonId'] }}-exercise-{{ e['ExerciseId'] }}"), [{
|
||||
x: {{ e['EstimatedOneRepMaxProgressions']['StartDates'] | replace('"', "'") | safe }},
|
||||
y: {{ e['EstimatedOneRepMaxProgressions']['Estimated1RMs'] | replace('"', "'") | safe }},
|
||||
text: {{ e['EstimatedOneRepMaxProgressions']['TopSets'] | replace('"', "'") | safe }},
|
||||
name: "{{ p['PersonName'] }} - {{ e['ExerciseName'] }}",
|
||||
hovertemplate
|
||||
}], layout, config);
|
||||
</script>
|
||||
{% endif %}
|
||||
<table class="min-w-full divide-y divide-gray-200">
|
||||
<thead class="bg-gray-50">
|
||||
<tr>
|
||||
<th scope="col"
|
||||
class="p-4 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
Date
|
||||
</th>
|
||||
<th scope="col"
|
||||
class="p-4 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
Rep Max
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="bg-white">
|
||||
|
||||
{% for rm in e['RepMaxes'] %}
|
||||
<tr>
|
||||
<td class="p-4 whitespace-nowrap text-sm font-normal text-gray-500">
|
||||
{{ rm['StartDate'] }}
|
||||
</td>
|
||||
<td class="p-4 whitespace-nowrap text-sm font-semibold text-gray-900">
|
||||
{{ rm['Repetitions'] }} x {{ rm['Weight'] }}kg
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
{{ render_partial('partials/stats.html', stats=model['Stats']) }}
|
||||
|
||||
{% endblock %}
|
||||
@@ -1,186 +0,0 @@
|
||||
<a hx-get="{{ url_for('get_calendar', person_id=person['PersonId']) }}" hx-target="#container"
|
||||
hx-vals='{"date": "{{ selected_date }}", "view": "{{ selected_view }}"}' hx-trigger="refreshView"
|
||||
id="refreshViewElement"></a>
|
||||
|
||||
<div class="flex flex-grow flex-col bg-white sm:rounded shadow overflow-hidden">
|
||||
<div class="flex items-center justify-between pt-2 pb-2">
|
||||
<div class="flex">
|
||||
<div class="flex ml-6">
|
||||
<button hx-get="{{ url_for('get_calendar', person_id=person['PersonId']) }}" hx-target="#container"
|
||||
hx-vals='{"date": "{{ previous_date }}"}' hx-include="[name='view']" hx-push-url="true">
|
||||
<svg class="w-6 h-6" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
|
||||
stroke="currentColor" data-darkreader-inline-stroke=""
|
||||
style="--darkreader-inline-stroke:currentColor;">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7">
|
||||
</path>
|
||||
</svg>
|
||||
</button>
|
||||
<button hx-get="{{ url_for('get_calendar', person_id=person['PersonId']) }}" hx-target="#container"
|
||||
hx-vals='{"date": "{{ next_date }}"}' hx-include="[name='view']" hx-push-url="true">
|
||||
<svg class="w-6 h-6" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
|
||||
stroke="currentColor" data-darkreader-inline-stroke=""
|
||||
style="--darkreader-inline-stroke:currentColor;">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7">
|
||||
</path>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{% if selected_view == 'month' %}
|
||||
<h2 class="ml-2 text-xl font-bold leading-none">{{ strftime(selected_date, '%B, %Y') }}</h2>
|
||||
{% else %}
|
||||
<h2 class="ml-2 text-xl font-bold leading-none">{{ strftime(selected_date, '%Y') }}</h2>
|
||||
{% endif %}
|
||||
<span
|
||||
class="bg-blue-100 text-blue-800 text-sm font-medium mr-2 px-2.5 py-0.5 rounded dark:bg-blue-200 dark:text-blue-800 ml-5">{{
|
||||
person['PersonName']}}</span>
|
||||
</div>
|
||||
|
||||
<div class="mr-4">
|
||||
<select id="workout-view-picker" data-te-select-init data-te-select-filter="true" data-te-select-size="lg"
|
||||
name="view" hx-get="{{ url_for('get_calendar', person_id=person['PersonId']) }}" hx-target="#container"
|
||||
hx-vals='{"date": "{{ selected_date }}"}' hx-push-url="true">
|
||||
<option value="month" {% if selected_view=='month' %}selected{% endif %}>Month</option>
|
||||
<option value="year" {% if selected_view=='year' %}selected{% endif %}>Year</option>
|
||||
<option value="all">All</option>
|
||||
</select>
|
||||
<script>
|
||||
te.Select.getOrCreateInstance(document.querySelector("#workout-view-picker")).setValue("{{ selected_view | safe }}");
|
||||
</script>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if selected_view == 'month' %}
|
||||
<div class="flex flex-col px-2 py-2 -mb-px">
|
||||
<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 i in range((end_date-start_date).days + 1) %}
|
||||
{% set date = start_date + timedelta(days=i) %}
|
||||
{% set workout =
|
||||
get_first_element_from_list_with_matching_attribute(person['Workouts'],
|
||||
'StartDate',
|
||||
date) %}
|
||||
<div class="{% if date == datetime.today().date() %}rounded-md border-4 border-green-50{% endif %} border flex flex-col h-50 sm:h-40 md:h-30 lg:h-30 mx-auto mx-auto overflow-hidden w-full pt-2 pl-1 cursor-pointer {% if selected_date.month != date.month %}bg-gray-100{% endif %}"
|
||||
{% if workout %}
|
||||
hx-get="{{ url_for('get_workout_modal', person_id=person['PersonId'], workout_id=workout['WorkoutId']) }}"
|
||||
hx-target='body' hx-swap='beforeend' {% endif %}>
|
||||
<div class="top h-5 w-full">
|
||||
<span class="text-gray-500 font-semibold">{{ date.day }}</span>
|
||||
</div>
|
||||
<div class="bottom flex-grow py-1 w-full">
|
||||
{% if workout['TopSets']|length > 0 %}
|
||||
{% for topset in workout['TopSets'] %}
|
||||
<button
|
||||
class="flex flex-col xl:flex-row items-start lg:items-center flex-shrink-0 px-0 sm:px-0.5 md:px-0.5 lg:px-0.5 text-xs">
|
||||
<span class="ml-0 sm:ml-0.5 md:ml-2 lg:ml-2 font-medium leading-none truncate">{{
|
||||
topset['ExerciseName'] }}</span>
|
||||
<span class="ml-0 sm:ml-0.5 md:ml-2 lg:ml-2 font-light leading-none">{{ topset['Repetitions'] }}
|
||||
x
|
||||
{{
|
||||
topset['Weight']
|
||||
}}kg</span>
|
||||
</button>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
{% elif selected_view == 'year'%}
|
||||
<div class="flex justify-center px-2 py-2 -mb-px">
|
||||
|
||||
<div
|
||||
class="grid grid-cols-1 lg:grid-cols-2 xl:grid-cols-3 2xl:grid-cols-4 gap-4 lg:gap-10 bg-white overflow-hidden">
|
||||
{% for i in range(12) %}
|
||||
{% set date = start_date + relativedelta(months=i) %}
|
||||
{% set first_day_of_month = date.replace(day=1) %}
|
||||
{% set last_day_of_month = date + relativedelta(day=31) %}
|
||||
{% set (first_day, last_day) = first_and_last_visible_days_in_month(first_day_of_month, last_day_of_month)
|
||||
%}
|
||||
<div>
|
||||
<div class="bg-grey-lighter font-semibold text-center cursor-pointer"
|
||||
hx-get="{{ url_for('get_calendar', person_id=person['PersonId']) }}" hx-target="#container"
|
||||
hx-vals='{"date": "{{ first_day_of_month }}", "view": "month"}' hx-push-url="true">{{
|
||||
strftime(first_day_of_month, '%B %Y') }}
|
||||
</div>
|
||||
<div>
|
||||
<div class="grid grid-cols-7 border-b font-semibold">
|
||||
<div class="py-3 px-4">S</div>
|
||||
<div class="py-3 px-4">M</div>
|
||||
<div class="py-3 px-4">T</div>
|
||||
<div class="py-3 px-4">W</div>
|
||||
<div class="py-3 px-4">T</div>
|
||||
<div class="py-3 px-4">F</div>
|
||||
<div class="py-3 px-4">S</div>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-7 overflow-hidden flex-1 pl-2 pr-2 w-full">
|
||||
{% for i in range((last_day-first_day).days + 1) %}
|
||||
{% set day_date = first_day + timedelta(days=i) %}
|
||||
{% set workout =
|
||||
get_first_element_from_list_with_matching_attribute(person['Workouts'],
|
||||
'StartDate',
|
||||
day_date) %}
|
||||
{% set is_in_month = day_date.month == first_day_of_month.month%}
|
||||
<div class="{% if day_date == datetime.today().date() and is_in_month %}rounded-md border-4 border-green-50 border{% endif %} py-3 px-4 hover:bg-blue hover:text-white text-center cursor-pointer rounded-md {% if workout and is_in_month %}bg-green-100{% endif %}"
|
||||
{% if workout %}
|
||||
hx-get="{{ url_for('get_workout_modal', person_id=person['PersonId'], workout_id=workout['WorkoutId']) }}"
|
||||
hx-target='body' hx-swap='beforeend' {% endif %}>
|
||||
{% if is_in_month %}
|
||||
{{ day_date.day }} {% endif %}</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{{ render_partial('partials/stats.html', stats=person['Stats']) }}
|
||||
|
||||
<button
|
||||
class="fixed z-90 bottom-10 right-8 bg-blue-600 w-20 h-20 rounded-full drop-shadow-lg flex justify-center items-center text-white text-4xl hover:bg-blue-700 hover:drop-shadow-2xl hover:animate-bounce duration-300"
|
||||
hx-post="{{ url_for('create_workout', person_id=person['PersonId']) }}" hx-target='body' hx-swap='beforeend'>
|
||||
<svg viewBox="0 0 20 20" enable-background="new 0 0 20 20" class="w-6 h-6 inline-block">
|
||||
<path fill="#FFFFFF" d="M16,10c0,0.553-0.048,1-0.601,1H11v4.399C11,15.951,10.553,16,10,16c-0.553,0-1-0.049-1-0.601V11H4.601
|
||||
C4.049,11,4,10.553,4,10c0-0.553,0.049-1,0.601-1H9V4.601C9,4.048,9.447,4,10,4c0.553,0,1,0.048,1,0.601V9h4.399
|
||||
C15.952,9,16,9.447,16,10z" />
|
||||
</svg>
|
||||
</button>
|
||||
@@ -1,176 +0,0 @@
|
||||
<div class="bg-white shadow rounded-lg p-4 w-full mb-4">
|
||||
<div class="flex flex-wrap">
|
||||
<div class="w-full lg:w-1/4 sm:w-full px-3 mb-6 md:mb-0">
|
||||
<div class="flex flex-wrap -mx-3">
|
||||
<div class="w-full px-3">
|
||||
<div class="mb-3 w-full"><label
|
||||
class="block uppercase tracking-wide text-gray-700 text-xs font-bold mb-2"
|
||||
for="grid-city">People</label>
|
||||
<select class="bg-gray-50 border border-gray-300 hidden" data-te-select-filter="true"
|
||||
data-te-select-init="" data-te-select-size="lg" hx-get="{{ url_for('dashboard') }}"
|
||||
hx-include="[name='min_date'],[name='max_date'],[name='exercise_id']" hx-push-url="true"
|
||||
hx-target="#container" id="dashboard-people-multi-select" multiple="" name="person_id">
|
||||
{% for p in people %}
|
||||
<option value="{{ p['PersonId'] }}">{{
|
||||
p['Name']
|
||||
}}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<script>te.Select.getOrCreateInstance(document.querySelector("#dashboard-people-multi-select")).setValue({{ list_to_string(selected_person_ids) | safe}})</script>
|
||||
</div>
|
||||
</div>
|
||||
<div class="w-full lg:w-1/4 sm:w-full px-3 mb-6 md:mb-0">
|
||||
<div class="flex flex-wrap -mx-3">
|
||||
<div class="w-full px-3">
|
||||
<div class="mb-3 w-full"><label
|
||||
class="block uppercase tracking-wide text-gray-700 text-xs font-bold mb-2"
|
||||
for="grid-city">Exercises</label>
|
||||
<select class="bg-gray-50 border border-gray-300 hidden" data-te-select-filter="true"
|
||||
data-te-select-init="" data-te-select-size="lg" hx-get="{{ url_for('dashboard') }}"
|
||||
hx-include="[name='min_date'],[name='max_date'],[name='person_id']" hx-push-url="true"
|
||||
hx-target="#container" id="dashboard-exercise-multi-select" multiple="" name="exercise_id">
|
||||
{% for e in exercises %}
|
||||
<option value="{{ e['ExerciseId'] }}">{{
|
||||
e['Name']
|
||||
}}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<script>te.Select.getOrCreateInstance(document.querySelector("#dashboard-exercise-multi-select")).setValue({{ list_to_string(selected_exercise_ids) | safe}})</script>
|
||||
</div>
|
||||
</div>
|
||||
<div class="w-full lg:w-1/4 sm:w-full px-3 mb-6 md:mb-0"><label
|
||||
class="block uppercase tracking-wide text-gray-700 text-xs font-bold mb-2" for="grid-city">Min
|
||||
date</label>
|
||||
<div class="relative">
|
||||
<div class="flex absolute inset-y-0 left-0 items-center pl-3 pointer-events-none"><svg
|
||||
class="w-5 h-5 text-gray-500 dark:text-gray-400" viewBox="0 0 20 20" aria-hidden="true"
|
||||
fill="currentColor" xmlns="http://www.w3.org/2000/svg" data-darkreader-inline-fill=""
|
||||
style="--darkreader-inline-fill:currentColor;">
|
||||
<path
|
||||
d="M6 2a1 1 0 00-1 1v1H4a2 2 0 00-2 2v10a2 2 0 002 2h12a2 2 0 002-2V6a2 2 0 00-2-2h-1V3a1 1 0 10-2 0v1H7V3a1 1 0 00-1-1zm0 5a1 1 0 000 2h8a1 1 0 100-2H6z"
|
||||
clip-rule="evenodd" fill-rule="evenodd"></path>
|
||||
</svg></div><input
|
||||
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full pl-10 p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500 w-full"
|
||||
hx-get="{{ url_for('dashboard') }}"
|
||||
hx-include="[name='min_date'],[name='max_date'],[name='person_id'],[name='exercise_id']"
|
||||
hx-push-url="true" hx-target="#container" hx-trigger="change" name="min_date" type="date"
|
||||
value="{{ min_date }}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="w-full lg:w-1/4 sm:w-full px-3 mb-6 md:mb-0"><label
|
||||
class="block uppercase tracking-wide text-gray-700 text-xs font-bold mb-2" for="grid-zip">Max
|
||||
date</label>
|
||||
<div class="relative">
|
||||
<div class="flex absolute inset-y-0 left-0 items-center pl-3 pointer-events-none"><svg
|
||||
class="w-5 h-5 text-gray-500 dark:text-gray-400" viewBox="0 0 20 20" aria-hidden="true"
|
||||
fill="currentColor" xmlns="http://www.w3.org/2000/svg" data-darkreader-inline-fill=""
|
||||
style="--darkreader-inline-fill:currentColor;">
|
||||
<path
|
||||
d="M6 2a1 1 0 00-1 1v1H4a2 2 0 00-2 2v10a2 2 0 002 2h12a2 2 0 002-2V6a2 2 0 00-2-2h-1V3a1 1 0 10-2 0v1H7V3a1 1 0 00-1-1zm0 5a1 1 0 000 2h8a1 1 0 100-2H6z"
|
||||
clip-rule="evenodd" fill-rule="evenodd"></path>
|
||||
</svg></div><input
|
||||
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full pl-10 p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500 w-full"
|
||||
hx-get="{{ url_for('dashboard') }}" hx-push-url="true" hx-target="#container" hx-trigger="change"
|
||||
name="max_date" type="date" value="{{ max_date }}">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% with person_id=None, tags=tags %}
|
||||
{% include 'partials/tags.html' %}
|
||||
{% endwith %}
|
||||
</div>
|
||||
|
||||
|
||||
{% if model['People']|length == 0 %}
|
||||
<div class="bg-purple-100 rounded-lg py-5 px-6 mb-4 text-base text-purple-700 mb-3" role="alert" id="no-workouts">
|
||||
No workouts selected.
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="w-full grid grid-cols-1 xl:grid-cols-3 2xl:grid-cols-3 gap-4">
|
||||
|
||||
{% for p in model['People'] %}
|
||||
<div class="bg-white shadow rounded-lg p-4 sm:p-6 xl:p-8 ">
|
||||
|
||||
<div class="mb-4 flex items-center justify-between">
|
||||
<div>
|
||||
<h3 class="text-xl font-bold text-gray-900 mb-2">{{ p['PersonName'] }}</h3>
|
||||
<span class="text-base font-normal text-gray-500">Current rep maxes</span>
|
||||
</div>
|
||||
<div class="flex-shrink-0">
|
||||
<a hx-get="{{ url_for('get_calendar' ,person_id=p['PersonId']) }}" hx-push-url="true"
|
||||
hx-target="#container"
|
||||
class="text-sm font-medium text-cyan-600 hover:bg-gray-100 rounded-lg p-2 cursor-pointer">View
|
||||
workouts</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if p['NumberOfWorkouts'] == 0 %}
|
||||
<div class="bg-purple-100 rounded-lg py-5 px-6 mb-4 text-base text-purple-700 mb-3" role="alert">
|
||||
No workouts completed.
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% for e in p['Exercises'] %}
|
||||
<div class="flex flex-col mt-8">
|
||||
<div class="overflow-x-auto rounded-lg">
|
||||
<div class="align-middle inline-block min-w-full">
|
||||
<div class="shadow overflow-hidden sm:rounded-lg">
|
||||
<h4 class="text-l font-semibold text-blue-400 mb-2 text-center">{{ e['ExerciseName'] }}</h4>
|
||||
{% if e['RepMaxes']|length > 1 %}
|
||||
<div id="person-{{ p['PersonId'] }}-exercise-{{ e['ExerciseId'] }}"
|
||||
class="w-full mt-2 aspect-video"></div>
|
||||
<script>
|
||||
Plotly.newPlot(document.getElementById("person-{{ p['PersonId'] }}-exercise-{{ e['ExerciseId'] }}"), [{
|
||||
x: {{ e['EstimatedOneRepMaxProgressions']['StartDates'] | replace('"', "'") | safe }},
|
||||
y: {{ e['EstimatedOneRepMaxProgressions']['Estimated1RMs'] | replace('"', "'") | safe }},
|
||||
text: {{ e['EstimatedOneRepMaxProgressions']['TopSets'] | replace('"', "'") | safe }},
|
||||
name: "{{ p['PersonName'] }} - {{ e['ExerciseName'] }}",
|
||||
hovertemplate
|
||||
}], layout, config);
|
||||
</script>
|
||||
{% endif %}
|
||||
<table class="min-w-full divide-y divide-gray-200">
|
||||
<thead class="bg-gray-50">
|
||||
<tr>
|
||||
<th scope="col"
|
||||
class="p-4 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
Date
|
||||
</th>
|
||||
<th scope="col"
|
||||
class="p-4 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
Rep Max
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="bg-white">
|
||||
|
||||
{% for rm in e['RepMaxes'] %}
|
||||
<tr>
|
||||
<td class="p-4 whitespace-nowrap text-sm font-normal text-gray-500">
|
||||
{{ rm['StartDate'] }}
|
||||
</td>
|
||||
<td class="p-4 whitespace-nowrap text-sm font-semibold text-gray-900">
|
||||
{{ rm['Repetitions'] }} x {{ rm['Weight'] }}kg
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
{{ render_partial('partials/stats.html', stats=model['Stats']) }}
|
||||
@@ -1,262 +0,0 @@
|
||||
<a hx-get="{{ url_for('get_person', person_id=person['PersonId']) }}"
|
||||
hx-include="[name='exercise_id'],[name='min_date'],[name='max_date']" hx-target="#container"
|
||||
hx-trigger="refreshView" id="refreshViewElement"></a>
|
||||
|
||||
<div class="flex justify-center">
|
||||
<div class="bg-white shadow rounded-lg p-4 sm:w-full xl:p-8 md:w-full lg:w-11/12">
|
||||
|
||||
<div class="mb-4 flex items-center justify-between">
|
||||
<div>
|
||||
<h3 class="text-xl font-bold text-gray-900 mb-2">{{ person['PersonName'] }}</h3>
|
||||
<span class="text-base font-normal text-gray-500">List of workouts</span>
|
||||
</div>
|
||||
<div>
|
||||
<div>
|
||||
<select id="workout-view-picker" data-te-select-init data-te-select-filter="true"
|
||||
data-te-select-size="lg" name="view"
|
||||
hx-get="{{ url_for('get_calendar', person_id=person['PersonId']) }}" hx-target="#container"
|
||||
hx-push-url="true">
|
||||
<option value="month">Month</option>
|
||||
<option value="year">Year</option>
|
||||
<option value="all" selected>All</option>
|
||||
</select>
|
||||
<script>
|
||||
te.Select.getOrCreateInstance(document.querySelector("#workout-view-picker")).setValue("all");
|
||||
</script>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="flex">
|
||||
{% set exercise_list = person['FilteredExercises'] %}
|
||||
|
||||
</div>
|
||||
|
||||
<div class="flex flex-wrap -mx-3 mb-1">
|
||||
<div class="w-full md:w-1/4 px-3 mb-6 md:mb-0">
|
||||
<div class="mb-1 w-full">
|
||||
<label class="block uppercase tracking-wide text-gray-700 text-xs font-bold mb-2" for="grid-city">
|
||||
Exercises
|
||||
</label>
|
||||
<select id="multiSelection" data-te-select-init data-te-select-filter="true"
|
||||
data-te-select-size="lg" name="exercise_id" class="bg-gray-50 border border-gray-300 " multiple
|
||||
hx-get="{{ url_for('get_person', person_id=person['PersonId']) }}"
|
||||
hx-include="[name='exercise_id'],[name='min_date'],[name='max_date'],[name='graph_axis']" hx-target="#container" hx-push-url="true">
|
||||
{% for e in person['Exercises'] %}
|
||||
<option value="{{ e['ExerciseId'] }}" {{ in_list(e['ExerciseId'],
|
||||
selected_exercise_ids, 'selected' ) }}>{{
|
||||
e['ExerciseName']
|
||||
}}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
<script>
|
||||
te.Select.getOrCreateInstance(document.querySelector("#multiSelection")).setValue({{ list_to_string(selected_exercise_ids) | safe }});
|
||||
</script>
|
||||
</div>
|
||||
<div class="w-full md:w-1/4 px-3 mb-6 md:mb-0">
|
||||
<label class="block uppercase tracking-wide text-gray-700 text-xs font-bold mb-2" for="grid-city">
|
||||
Min date
|
||||
</label>
|
||||
<div class="relative">
|
||||
<div class="flex absolute inset-y-0 left-0 items-center pl-3 pointer-events-none">
|
||||
<svg aria-hidden="true" class="w-5 h-5 text-gray-500 dark:text-gray-400" fill="currentColor"
|
||||
viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd"
|
||||
d="M6 2a1 1 0 00-1 1v1H4a2 2 0 00-2 2v10a2 2 0 002 2h12a2 2 0 002-2V6a2 2 0 00-2-2h-1V3a1 1 0 10-2 0v1H7V3a1 1 0 00-1-1zm0 5a1 1 0 000 2h8a1 1 0 100-2H6z"
|
||||
clip-rule="evenodd"></path>
|
||||
</svg>
|
||||
</div>
|
||||
<input type="date"
|
||||
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full pl-10 p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500 w-full"
|
||||
name="min_date" value="{{ min_date }}"
|
||||
hx-get="{{ url_for('get_person', person_id=person['PersonId']) }}"
|
||||
hx-include="[name='exercise_id'],[name='min_date'],[name='max_date'],[name='graph_axis']" hx-target="#container"
|
||||
hx-push-url="true" hx-trigger="change">
|
||||
</div>
|
||||
</div>
|
||||
<div class="w-full md:w-1/4 px-3 mb-6 md:mb-0">
|
||||
<label class="block uppercase tracking-wide text-gray-700 text-xs font-bold mb-2" for="grid-zip">
|
||||
Max date
|
||||
</label>
|
||||
<div class="relative">
|
||||
<div class="flex absolute inset-y-0 left-0 items-center pl-3 pointer-events-none">
|
||||
<svg aria-hidden="true" class="w-5 h-5 text-gray-500 dark:text-gray-400" fill="currentColor"
|
||||
viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd"
|
||||
d="M6 2a1 1 0 00-1 1v1H4a2 2 0 00-2 2v10a2 2 0 002 2h12a2 2 0 002-2V6a2 2 0 00-2-2h-1V3a1 1 0 10-2 0v1H7V3a1 1 0 00-1-1zm0 5a1 1 0 000 2h8a1 1 0 100-2H6z"
|
||||
clip-rule="evenodd"></path>
|
||||
</svg>
|
||||
</div>
|
||||
<input type="date"
|
||||
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full pl-10 p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500 w-full"
|
||||
name="max_date" value="{{ max_date }}"
|
||||
hx-get="{{ url_for('get_person', person_id=person['PersonId']) }}"
|
||||
hx-include="[name='exercise_id'],[name='min_date'],[name='max_date'],[name='graph_axis']" hx-target="#container"
|
||||
hx-push-url="true" hx-trigger="change">
|
||||
</div>
|
||||
</div>
|
||||
<div class="w-full md:w-1/4 px-3 mb-6 md:mb-0">
|
||||
<div class="mb-1 w-full">
|
||||
<label class="block uppercase tracking-wide text-gray-700 text-xs font-bold mb-2" for="grid-city">
|
||||
Graph Axis
|
||||
</label>
|
||||
<select id="graph-axis-multiselect" data-te-select-init data-te-select-filter="true"
|
||||
data-te-select-size="lg" name="graph_axis" class="bg-gray-50 border border-gray-300 " multiple
|
||||
hx-get="{{ url_for('get_person', person_id=person['PersonId']) }}"
|
||||
hx-include="[name='exercise_id'],[name='min_date'],[name='max_date'],[name='graph_axis']" hx-target="#container" hx-push-url="true">
|
||||
<option value="repetitions">Repetitions</option>
|
||||
<option value="weight">Weigh</option>
|
||||
<option value="estimated1rm">Estimated 1RM</option>
|
||||
</select>
|
||||
</div>
|
||||
<script>
|
||||
te.Select.getOrCreateInstance(document.querySelector("#graph-axis-multiselect")).setValue({{ graph_axis | safe }});
|
||||
</script>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% with person_id=person['PersonId'], tags=tags %}
|
||||
{% include 'partials/tags.html' %}
|
||||
{% endwith %}
|
||||
|
||||
<div class="flex flex-col mt-3">
|
||||
<div class="overflow-x-auto rounded-lg">
|
||||
<div class="align-middle inline-block min-w-full">
|
||||
<div class="shadow overflow-hidden sm:rounded-lg">
|
||||
|
||||
{% if person['Workouts']|length > 0 %}
|
||||
<table class="min-w-full divide-y divide-gray-200">
|
||||
<thead class="bg-gray-50">
|
||||
<tr>
|
||||
<th scope="col"
|
||||
class="p-4 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
Date
|
||||
</th>
|
||||
{% for e in exercise_list %}
|
||||
<th scope="col"
|
||||
class="p-4 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
{{ e['ExerciseName'] }}
|
||||
</th>
|
||||
{% endfor %}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="bg-white">
|
||||
|
||||
{% for w in person['Workouts'] %}
|
||||
<tr hx-get="{{ url_for('get_workout_modal', person_id=person['PersonId'], workout_id=w['WorkoutId']) }}"
|
||||
hx-target='body' hx-swap='beforeend' class="cursor-pointer">
|
||||
<td class="p-4 whitespace-nowrap text-sm font-normal text-gray-500">
|
||||
{{ strftime(w['StartDate'], "%b %d %Y") }}
|
||||
</td>
|
||||
|
||||
{% for e in exercise_list %}
|
||||
<td class="p-4 whitespace-nowrap text-sm font-semibold text-gray-900">
|
||||
{% set topset_exercise =
|
||||
get_first_element_from_list_with_matching_attribute(w['TopSets'],
|
||||
'ExerciseId',
|
||||
e['ExerciseId']) %}
|
||||
{% if topset_exercise %}
|
||||
{{ topset_exercise['Repetitions'] }} x {{ topset_exercise['Weight'] }}kg
|
||||
{% endif %}
|
||||
</td>
|
||||
{% endfor %}
|
||||
</tr>
|
||||
{% endfor %}
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
{% endif %}
|
||||
|
||||
{% if person['Workouts']|length == 0 %}
|
||||
<div class="bg-purple-100 rounded-lg py-5 px-6 mb-4 text-base text-purple-700 mb-3"
|
||||
role="alert">
|
||||
No workouts found.
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if graph_axis %}
|
||||
<div class="mt-4 mb-4 w-full grid grid-cols-1 2xl:grid-cols-2 gap-4">
|
||||
{% for exercise_graph in person['ExerciseGraphs'] %}
|
||||
<div class="bg-white shadow rounded-lg p-4 sm:p-6 xl:p-8 ">
|
||||
<div class="flex flex-col items-center">
|
||||
<div class="flex-shrink-0">
|
||||
<span class="text-2xl sm:text-3xl leading-none font-bold text-gray-900">{{
|
||||
exercise_graph['ExerciseName'] }}</span>
|
||||
</div>
|
||||
<div id="e-{{ exercise_graph['ExerciseId'] }}"></div>
|
||||
<script>
|
||||
Plotly.newPlot("e-{{ exercise_graph['ExerciseId'] }}", [
|
||||
{% if 'repetitions' in graph_axis %}
|
||||
{
|
||||
x: {{ exercise_graph['StartDates'] | replace('"', "'") | safe }},
|
||||
y: {{ exercise_graph['Repetitions'] | replace('"', "'") | safe }},
|
||||
name: 'Reps',
|
||||
type: 'scatter'
|
||||
},
|
||||
{% endif %}
|
||||
{% if 'weight' in graph_axis %}
|
||||
{
|
||||
x: {{ exercise_graph['StartDates'] | replace('"', "'") | safe }},
|
||||
y: {{ exercise_graph['Weights'] | replace('"', "'") | safe }},
|
||||
name: 'Weight',
|
||||
yaxis: 'y2',
|
||||
type: 'scatter'
|
||||
},
|
||||
{% endif %}
|
||||
{% if 'estimated1rm' in graph_axis %}
|
||||
{
|
||||
x: {{ exercise_graph['StartDates'] | replace('"', "'") | safe }},
|
||||
y: {{ exercise_graph['Estimated1RM'] | replace('"', "'") | safe }},
|
||||
name: 'Estimated 1RM',
|
||||
yaxis: 'y3',
|
||||
type: 'scatter'
|
||||
},
|
||||
{% endif %}
|
||||
],
|
||||
{
|
||||
margin: { t: 0, l: 20, r: 20 },
|
||||
xaxis: { type: 'date', showgrid: false },
|
||||
yaxis: { title: 'Reps', showgrid: false },
|
||||
yaxis2: {
|
||||
title: 'Weight',
|
||||
overlaying: 'y',
|
||||
side: 'right',
|
||||
showgrid: false,
|
||||
position: 0.96
|
||||
},
|
||||
yaxis3: {
|
||||
title: 'Estimated 1RM',
|
||||
overlaying: 'y',
|
||||
side: 'right',
|
||||
showgrid: false,
|
||||
position: 1.00
|
||||
}
|
||||
}, config);
|
||||
</script>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{{ render_partial('partials/stats.html', stats=person['Stats']) }}
|
||||
|
||||
<button
|
||||
class="fixed z-90 bottom-10 right-8 bg-blue-600 w-20 h-20 rounded-full drop-shadow-lg flex justify-center items-center text-white text-4xl hover:bg-blue-700 hover:drop-shadow-2xl hover:animate-bounce duration-300"
|
||||
hx-post="{{ url_for('create_workout', person_id=person['PersonId']) }}" hx-target='body' hx-swap='beforeend'>
|
||||
<svg viewBox="0 0 20 20" enable-background="new 0 0 20 20" class="w-6 h-6 inline-block">
|
||||
<path fill="#FFFFFF" d="M16,10c0,0.553-0.048,1-0.601,1H11v4.399C11,15.951,10.553,16,10,16c-0.553,0-1-0.049-1-0.601V11H4.601
|
||||
C4.049,11,4,10.553,4,10c0-0.553,0.049-1,0.601-1H9V4.601C9,4.048,9.447,4,10,4c0.553,0,1,0.048,1,0.601V9h4.399
|
||||
C15.952,9,16,9.447,16,10z" />
|
||||
</svg>
|
||||
</button>
|
||||
@@ -2,8 +2,267 @@
|
||||
|
||||
{% block content %}
|
||||
|
||||
{{ render_partial('partials/page/person.html',
|
||||
person=person, selected_exercise_ids=selected_exercise_ids, max_date=max_date,
|
||||
min_date=min_date, tags=tags, graph_axis=graph_axis) }}
|
||||
<a hx-get="{{ url_for('get_person', person_id=person['PersonId']) }}"
|
||||
hx-include="[name='exercise_id'],[name='min_date'],[name='max_date']" hx-target="#container"
|
||||
hx-trigger="refreshView" id="refreshViewElement"></a>
|
||||
|
||||
<div class="flex justify-center">
|
||||
<div class="bg-white shadow rounded-lg p-4 sm:w-full xl:p-8 md:w-full lg:w-11/12">
|
||||
|
||||
<div class="mb-4 flex items-center justify-between">
|
||||
<div>
|
||||
<h3 class="text-xl font-bold text-gray-900 mb-2">{{ person['PersonName'] }}</h3>
|
||||
<span class="text-base font-normal text-gray-500">List of workouts</span>
|
||||
</div>
|
||||
<div>
|
||||
<div>
|
||||
<select id="workout-view-picker" data-te-select-init data-te-select-filter="true"
|
||||
data-te-select-size="lg" name="view"
|
||||
hx-get="{{ url_for('get_calendar', person_id=person['PersonId']) }}" hx-target="#container"
|
||||
hx-push-url="true">
|
||||
<option value="month">Month</option>
|
||||
<option value="year">Year</option>
|
||||
<option value="all" selected>All</option>
|
||||
</select>
|
||||
<script>
|
||||
te.Select.getOrCreateInstance(document.querySelector("#workout-view-picker")).setValue("all");
|
||||
</script>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="flex">
|
||||
{% set exercise_list = person['FilteredExercises'] %}
|
||||
|
||||
</div>
|
||||
|
||||
<div class="flex flex-wrap -mx-3 mb-1">
|
||||
<div class="w-full md:w-1/4 px-3 mb-6 md:mb-0">
|
||||
<div class="mb-1 w-full">
|
||||
<label class="block uppercase tracking-wide text-gray-700 text-xs font-bold mb-2" for="grid-city">
|
||||
Exercises
|
||||
</label>
|
||||
<select id="multiSelection" data-te-select-init data-te-select-filter="true"
|
||||
data-te-select-size="lg" name="exercise_id" class="bg-gray-50 border border-gray-300 " multiple
|
||||
hx-get="{{ url_for('get_person', person_id=person['PersonId']) }}"
|
||||
hx-include="[name='exercise_id'],[name='min_date'],[name='max_date'],[name='graph_axis']"
|
||||
hx-target="#container" hx-push-url="true">
|
||||
{% for e in person['Exercises'] %}
|
||||
<option value="{{ e['ExerciseId'] }}">{{
|
||||
e['ExerciseName']
|
||||
}}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
<script>
|
||||
te.Select.getOrCreateInstance(document.querySelector("#multiSelection")).setValue({{ selected_exercise_ids| list_to_string | safe }});
|
||||
</script>
|
||||
</div>
|
||||
<div class="w-full md:w-1/4 px-3 mb-6 md:mb-0">
|
||||
<label class="block uppercase tracking-wide text-gray-700 text-xs font-bold mb-2" for="grid-city">
|
||||
Min date
|
||||
</label>
|
||||
<div class="relative">
|
||||
<div class="flex absolute inset-y-0 left-0 items-center pl-3 pointer-events-none">
|
||||
<svg aria-hidden="true" class="w-5 h-5 text-gray-500 dark:text-gray-400" fill="currentColor"
|
||||
viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd"
|
||||
d="M6 2a1 1 0 00-1 1v1H4a2 2 0 00-2 2v10a2 2 0 002 2h12a2 2 0 002-2V6a2 2 0 00-2-2h-1V3a1 1 0 10-2 0v1H7V3a1 1 0 00-1-1zm0 5a1 1 0 000 2h8a1 1 0 100-2H6z"
|
||||
clip-rule="evenodd"></path>
|
||||
</svg>
|
||||
</div>
|
||||
<input type="date"
|
||||
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full pl-10 p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500 w-full"
|
||||
name="min_date" value="{{ min_date }}"
|
||||
hx-get="{{ url_for('get_person', person_id=person['PersonId']) }}"
|
||||
hx-include="[name='exercise_id'],[name='min_date'],[name='max_date'],[name='graph_axis']"
|
||||
hx-target="#container" hx-push-url="true" hx-trigger="change">
|
||||
</div>
|
||||
</div>
|
||||
<div class="w-full md:w-1/4 px-3 mb-6 md:mb-0">
|
||||
<label class="block uppercase tracking-wide text-gray-700 text-xs font-bold mb-2" for="grid-zip">
|
||||
Max date
|
||||
</label>
|
||||
<div class="relative">
|
||||
<div class="flex absolute inset-y-0 left-0 items-center pl-3 pointer-events-none">
|
||||
<svg aria-hidden="true" class="w-5 h-5 text-gray-500 dark:text-gray-400" fill="currentColor"
|
||||
viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd"
|
||||
d="M6 2a1 1 0 00-1 1v1H4a2 2 0 00-2 2v10a2 2 0 002 2h12a2 2 0 002-2V6a2 2 0 00-2-2h-1V3a1 1 0 10-2 0v1H7V3a1 1 0 00-1-1zm0 5a1 1 0 000 2h8a1 1 0 100-2H6z"
|
||||
clip-rule="evenodd"></path>
|
||||
</svg>
|
||||
</div>
|
||||
<input type="date"
|
||||
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full pl-10 p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500 w-full"
|
||||
name="max_date" value="{{ max_date }}"
|
||||
hx-get="{{ url_for('get_person', person_id=person['PersonId']) }}"
|
||||
hx-include="[name='exercise_id'],[name='min_date'],[name='max_date'],[name='graph_axis']"
|
||||
hx-target="#container" hx-push-url="true" hx-trigger="change">
|
||||
</div>
|
||||
</div>
|
||||
<div class="w-full md:w-1/4 px-3 mb-6 md:mb-0">
|
||||
<div class="mb-1 w-full">
|
||||
<label class="block uppercase tracking-wide text-gray-700 text-xs font-bold mb-2" for="grid-city">
|
||||
Graph Axis
|
||||
</label>
|
||||
<select id="graph-axis-multiselect" data-te-select-init data-te-select-filter="true"
|
||||
data-te-select-size="lg" name="graph_axis" class="bg-gray-50 border border-gray-300 " multiple
|
||||
hx-get="{{ url_for('get_person', person_id=person['PersonId']) }}"
|
||||
hx-include="[name='exercise_id'],[name='min_date'],[name='max_date'],[name='graph_axis']"
|
||||
hx-target="#container" hx-push-url="true">
|
||||
<option value="repetitions">Repetitions</option>
|
||||
<option value="weight">Weigh</option>
|
||||
<option value="estimated1rm">Estimated 1RM</option>
|
||||
</select>
|
||||
</div>
|
||||
<script>
|
||||
te.Select.getOrCreateInstance(document.querySelector("#graph-axis-multiselect")).setValue({{ graph_axis | safe }});
|
||||
</script>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% with person_id=person['PersonId'], tags=tags %}
|
||||
{% include 'partials/tags.html' %}
|
||||
{% endwith %}
|
||||
|
||||
<div class="flex flex-col mt-3">
|
||||
<div class="overflow-x-auto rounded-lg">
|
||||
<div class="align-middle inline-block min-w-full">
|
||||
<div class="shadow overflow-hidden sm:rounded-lg">
|
||||
|
||||
{% if person['Workouts']|length > 0 %}
|
||||
<table class="min-w-full divide-y divide-gray-200">
|
||||
<thead class="bg-gray-50">
|
||||
<tr>
|
||||
<th scope="col"
|
||||
class="p-4 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
Date
|
||||
</th>
|
||||
{% for e in exercise_list %}
|
||||
<th scope="col"
|
||||
class="p-4 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
{{ e['ExerciseName'] }}
|
||||
</th>
|
||||
{% endfor %}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="bg-white">
|
||||
|
||||
{% for w in person['Workouts'] %}
|
||||
<tr hx-get="{{ url_for('get_workout_modal', person_id=person['PersonId'], workout_id=w['WorkoutId']) }}"
|
||||
hx-target='body' hx-swap='beforeend' class="cursor-pointer">
|
||||
<td class="p-4 whitespace-nowrap text-sm font-normal text-gray-500">
|
||||
{{ w['StartDate'] | strftime("%b %d %Y") }}
|
||||
</td>
|
||||
|
||||
{% for e in exercise_list %}
|
||||
<td class="p-4 whitespace-nowrap text-sm font-semibold text-gray-900">
|
||||
{% set topset_exercise = w['TopSets'] |
|
||||
get_first_element_from_list_with_matching_attribute('ExerciseId',
|
||||
e['ExerciseId']) %}
|
||||
{% if topset_exercise %}
|
||||
{{ topset_exercise['Repetitions'] }} x {{ topset_exercise['Weight'] }}kg
|
||||
{% endif %}
|
||||
</td>
|
||||
{% endfor %}
|
||||
</tr>
|
||||
{% endfor %}
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
{% endif %}
|
||||
|
||||
{% if person['Workouts']|length == 0 %}
|
||||
<div class="bg-purple-100 rounded-lg py-5 px-6 mb-4 text-base text-purple-700 mb-3"
|
||||
role="alert">
|
||||
No workouts found.
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if graph_axis %}
|
||||
<div class="mt-4 mb-4 w-full grid grid-cols-1 2xl:grid-cols-2 gap-4">
|
||||
{% for exercise_graph in person['ExerciseGraphs'] %}
|
||||
<div class="bg-white shadow rounded-lg p-4 sm:p-6 xl:p-8 ">
|
||||
<div class="flex flex-col items-center">
|
||||
<div class="flex-shrink-0">
|
||||
<span class="text-2xl sm:text-3xl leading-none font-bold text-gray-900">{{
|
||||
exercise_graph['ExerciseName'] }}</span>
|
||||
</div>
|
||||
<div id="e-{{ exercise_graph['ExerciseId'] }}"></div>
|
||||
<script>
|
||||
Plotly.newPlot("e-{{ exercise_graph['ExerciseId'] }}", [
|
||||
{% if 'repetitions' in graph_axis %}
|
||||
{
|
||||
x: { { exercise_graph['StartDates'] | replace('"', "'") | safe } },
|
||||
y: { { exercise_graph['Repetitions'] | replace('"', "'") | safe } },
|
||||
name: 'Reps',
|
||||
type: 'scatter'
|
||||
},
|
||||
{% endif %}
|
||||
{% if 'weight' in graph_axis %}
|
||||
{
|
||||
x: { { exercise_graph['StartDates'] | replace('"', "'") | safe } },
|
||||
y: { { exercise_graph['Weights'] | replace('"', "'") | safe } },
|
||||
name: 'Weight',
|
||||
yaxis: 'y2',
|
||||
type: 'scatter'
|
||||
},
|
||||
{% endif %}
|
||||
{% if 'estimated1rm' in graph_axis %}
|
||||
{
|
||||
x: { { exercise_graph['StartDates'] | replace('"', "'") | safe } },
|
||||
y: { { exercise_graph['Estimated1RM'] | replace('"', "'") | safe } },
|
||||
name: 'Estimated 1RM',
|
||||
yaxis: 'y3',
|
||||
type: 'scatter'
|
||||
},
|
||||
{% endif %}
|
||||
],
|
||||
{
|
||||
margin: { t: 0, l: 20, r: 20 },
|
||||
xaxis: { type: 'date', showgrid: false },
|
||||
yaxis: { title: 'Reps', showgrid: false },
|
||||
yaxis2: {
|
||||
title: 'Weight',
|
||||
overlaying: 'y',
|
||||
side: 'right',
|
||||
showgrid: false,
|
||||
position: 0.96
|
||||
},
|
||||
yaxis3: {
|
||||
title: 'Estimated 1RM',
|
||||
overlaying: 'y',
|
||||
side: 'right',
|
||||
showgrid: false,
|
||||
position: 1.00
|
||||
}
|
||||
}, config);
|
||||
</script>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{{ render_partial('partials/stats.html', stats=person['Stats']) }}
|
||||
|
||||
<button
|
||||
class="fixed z-90 bottom-10 right-8 bg-blue-600 w-20 h-20 rounded-full drop-shadow-lg flex justify-center items-center text-white text-4xl hover:bg-blue-700 hover:drop-shadow-2xl hover:animate-bounce duration-300"
|
||||
hx-post="{{ url_for('create_workout', person_id=person['PersonId']) }}" hx-target='body' hx-swap='beforeend'>
|
||||
<svg viewBox="0 0 20 20" enable-background="new 0 0 20 20" class="w-6 h-6 inline-block">
|
||||
<path fill="#FFFFFF" d="M16,10c0,0.553-0.048,1-0.601,1H11v4.399C11,15.951,10.553,16,10,16c-0.553,0-1-0.049-1-0.601V11H4.601
|
||||
C4.049,11,4,10.553,4,10c0-0.553,0.049-1,0.601-1H9V4.601C9,4.048,9.447,4,10,4c0.553,0,1,0.048,1,0.601V9h4.399
|
||||
C15.952,9,16,9.447,16,10z" />
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user