Reorganize workout view for desktop and compact session details
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
<form class="w-full" id="new-set-workout-{{ workout_id }}"
|
||||
hx-post="{{ url_for('workout.create_topset', person_id=person_id, workout_id=workout_id) }}" hx-swap="beforeend"
|
||||
hx-target="#new-workout" _="on htmx:afterOnLoad
|
||||
if #no-workouts add .hidden to #no-workouts end
|
||||
|
||||
if detail.xhr.status == 200
|
||||
set #validation-error-{{ workout_id }}.innerText to ''
|
||||
add .hidden to #validation-error-{{ workout_id }}
|
||||
@@ -19,16 +19,16 @@
|
||||
<div id="validation-error-{{ workout_id }}"
|
||||
class="hidden text-red-500 text-xs italic mb-4 p-2 bg-red-50 border border-red-200 rounded"></div>
|
||||
|
||||
<div class="flex flex-wrap -mx-3 mb-2">
|
||||
<div class="w-full md:w-[30%] px-2 md:px-3 mb-6 md:mb-0">
|
||||
<label class="block uppercase tracking-wide text-gray-700 text-xs font-bold mb-2" for="grid-state">
|
||||
<div class="workout-set-fields">
|
||||
<div class="workout-set-field">
|
||||
<label class="block uppercase tracking-wide text-gray-700 text-xs font-bold mb-2" for="exercise-search">
|
||||
Exercise
|
||||
</label>
|
||||
{{ render_partial('partials/exercise/exercise_select.html', person_id=person_id,
|
||||
exercise_id=exercise_id, exercise_name=exercise_name) }}
|
||||
</div>
|
||||
|
||||
<div class="w-full md:w-[30%] px-2 md:px-3 mb-6 md:mb-0">
|
||||
<div class="workout-set-field">
|
||||
<label class="block uppercase tracking-wide text-gray-700 text-xs font-bold mb-2" for="grid-city">
|
||||
Reps
|
||||
</label>
|
||||
@@ -38,9 +38,9 @@
|
||||
_="on clearNewSetInputs set my.placeholder to ''" {% endif %}>
|
||||
</div>
|
||||
|
||||
<div class="w-full md:w-[30%] px-2 md:px-3 mb-6 md:mb-0">
|
||||
<div class="workout-set-field">
|
||||
<label class="block uppercase tracking-wide text-gray-700 text-xs font-bold mb-2" for="grid-zip">
|
||||
Weight
|
||||
Weight (kg)
|
||||
</label>
|
||||
<input
|
||||
class="appearance-none block w-full bg-gray-200 text-gray-700 border border-gray-200 rounded py-3 px-4 leading-tight focus:outline-none focus:bg-white focus:border-gray-500"
|
||||
@@ -48,14 +48,15 @@
|
||||
_="on clearNewSetInputs set my.placeholder to ''" {% endif %}>
|
||||
</div>
|
||||
|
||||
<div class="w-full md:w-[10%] px-2 md:px-3 mb-6 md:mb-0">
|
||||
<div class="workout-set-submit">
|
||||
<button
|
||||
class="flex items-center justify-center py-2 px-2 md:px-3 mb-3 text-sm font-medium text-center text-gray-900 bg-cyan-600 hover:bg-cyan-700 rounded-lg border border-gray-300 hover:scale-[1.02] transition-transform mb-6 md:mb-0 mt-0 md:mt-6 w-full"
|
||||
class="workout-add-button"
|
||||
type="submit">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 24 24" class="w-7 h-7">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 24 24" class="w-5 h-5">
|
||||
<path
|
||||
d="M12 4a1 1 0 011 1v6h6a1 1 0 110 2h-6v6a1 1 0 11-2 0v-6H5a1 1 0 110-2h6V5a1 1 0 011-1z" />
|
||||
</svg>
|
||||
<span>Add set</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -66,17 +67,18 @@
|
||||
|
||||
<div hx-trigger="exerciseSelected from:body"
|
||||
hx-get="{{ url_for('workout.get_most_recent_topset_for_exercise', person_id=person_id, workout_id=workout_id) }}"
|
||||
hx-target="#new-set-form-container-{{ workout_id }}" hx-include="[name='exercise_id']">
|
||||
hx-target="#new-set-form-container-{{ workout_id }}" hx-swap="outerHTML" hx-include="[name='exercise_id']">
|
||||
</div>
|
||||
|
||||
{% if exercise_id %}
|
||||
<div class="flex items-center justify-center">
|
||||
<details class="workout-previous-details">
|
||||
<summary>Previous performance{% if repetitions %}<span>{{ repetitions }} × {{ weight }} kg</span>{% endif %}</summary>
|
||||
<div class="w-full">
|
||||
<div class="hidden"
|
||||
hx-get="{{ url_for('get_exercise_progress_for_user', person_id=person_id, exercise_id=exercise_id) }}"
|
||||
hx-trigger="load" hx-target="this" hx-swap="outerHTML">
|
||||
hx-trigger="toggle once from:closest details" hx-target="this" hx-swap="outerHTML">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</details>
|
||||
{% endif %}
|
||||
</div>
|
||||
@@ -1,12 +1,12 @@
|
||||
<tr id="topset-{{ topset_id }}">
|
||||
<tr class="workout-set-row" id="topset-{{ topset_id }}">
|
||||
<td class="p-0 sm:p-4 text-sm font-semibold text-gray-900 break-normal">
|
||||
{% if is_edit|default(false, true) == false %}
|
||||
<div class="flex items-center space-x-2">
|
||||
<div class="workout-exercise-name">
|
||||
<span class="cursor-pointer" hx-get="{{ url_for('tags.goto_tag') }}"
|
||||
hx-vals='{"filter": "?exercise_id={{ exercise_id }}", "person_id" : "{{ person_id }}" }'
|
||||
hx-target="#container" hx-swap="innerHTML" hx-push-url="true"
|
||||
_='on click trigger closeModalWithoutRefresh'>{{ exercise_name }}</span>
|
||||
<div class="flex flex-col sm:flex-row space-y-1 sm:space-y-0 sm:space-x-1">
|
||||
<div class="workout-exercise-tools">
|
||||
<button
|
||||
class="inline-flex justify-center p-1 text-gray-500 rounded cursor-pointer hover:text-gray-900 hover:bg-gray-100 dark:text-gray-400 dark:hover:text-white dark:hover:bg-gray-600"
|
||||
title="Show Progress Graph"
|
||||
@@ -17,7 +17,7 @@
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M13 7h8m0 0v8m0-8l-8 8-4-4-6 6"></path>
|
||||
</svg>
|
||||
<span class="sr-only">Show Progress Graph</span>
|
||||
<span class="workout-tool-label">Progress</span>
|
||||
</button>
|
||||
<button
|
||||
class="inline-flex justify-center p-1 text-gray-500 rounded cursor-pointer hover:text-gray-900 hover:bg-gray-100 dark:text-gray-400 dark:hover:text-white dark:hover:bg-gray-600"
|
||||
@@ -29,7 +29,7 @@
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M4 6h16M4 10h16M4 14h16M4 18h16"></path>
|
||||
</svg>
|
||||
<span class="sr-only">Show History</span>
|
||||
<span class="workout-tool-label">History</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -57,11 +57,11 @@
|
||||
<div class="flex items-center flex-col sm:flex-row">
|
||||
<input type="number"
|
||||
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-4 sm:pl-10 p-2.5 "
|
||||
name="repetitions" value="{{ repetitions }}">
|
||||
aria-label="Repetitions" name="repetitions" value="{{ repetitions }}">
|
||||
<p class="px-2">x</p>
|
||||
<input type="number"
|
||||
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-4 sm:pl-10 p-2.5 "
|
||||
name="weight" value="{{ weight }}">
|
||||
aria-label="Weight in kilograms" step="any" name="weight" value="{{ weight }}">
|
||||
<p class="px-2">kg</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
@@ -118,7 +118,7 @@
|
||||
</td>
|
||||
</tr>
|
||||
{# Target row modified for dismissible extra content (graph or history) #}
|
||||
<tr id="extra-target-{{ topset_id }}">
|
||||
<tr id="extra-target-{{ topset_id }}" {% if existing_row|default(false) or is_edit|default(false) %}hx-swap-oob="outerHTML"{% endif %}>
|
||||
<td colspan="3" class="p-0 relative">
|
||||
<div id="extra-content-{{ topset_id }}" class="extra-content-container" _="
|
||||
on htmx:afterSwap
|
||||
|
||||
@@ -1,64 +1,18 @@
|
||||
{% set distribution = distribution or muscle_distribution %}
|
||||
{% set category_name = category_name or 'Muscle Group' %}
|
||||
{% set distribution = distribution|default([], true) %}
|
||||
{% set category_name = category_name|default('Muscle Group', true) %}
|
||||
{% set breakdown_id = category_name.lower().replace(' ', '-') %}
|
||||
|
||||
{% if distribution %}
|
||||
<div class="px-4 py-3 bg-white relative" id="{{ breakdown_id }}-breakdown"
|
||||
<div class="workout-breakdown" id="{{ breakdown_id }}-breakdown"
|
||||
hx-get="{{ url_for('workout.get_workout_distribution', person_id=person_id, workout_id=workout_id, category=category_name) }}"
|
||||
hx-trigger="topsetAdded from:body" hx-swap="outerHTML">
|
||||
|
||||
<!-- Shared Popover Container (managed by Hyperscript) -->
|
||||
<div id="popover-{{ breakdown_id }}"
|
||||
class="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 z-30 hidden bg-white border border-gray-200 shadow-2xl rounded-xl p-4 min-w-[200px] animate-in fade-in zoom-in duration-200"
|
||||
_="on click from elsewhere add .hidden to me">
|
||||
<div id="content-{{ breakdown_id }}"></div>
|
||||
<button class="absolute top-2 right-2 text-gray-400 hover:text-gray-600"
|
||||
_="on click add .hidden to #popover-{{ breakdown_id }}">
|
||||
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path>
|
||||
</svg>
|
||||
</button>
|
||||
hx-trigger="topsetAdded from:body, topsetChanged from:body" hx-swap="outerHTML">
|
||||
<h3>{{ category_name }}</h3>
|
||||
{% if distribution %}
|
||||
<div class="workout-distribution-bar" aria-hidden="true">
|
||||
{% for item in distribution %}<span style="width: {{ item.percentage }}%; background-color: {{ item.color }};"></span>{% endfor %}
|
||||
</div>
|
||||
|
||||
<div class="flex items-center justify-between mb-2">
|
||||
<h4 class="text-[9px] font-black text-gray-400 uppercase tracking-[0.25em]">{{ category_name }} Distribution
|
||||
</h4>
|
||||
</div>
|
||||
|
||||
<!-- Sleek Performance Bar -->
|
||||
<div class="w-full h-8 flex overflow-hidden rounded-lg bg-gray-100 shadow-inner p-0.5 cursor-pointer">
|
||||
<ul class="workout-distribution-legend">
|
||||
{% for item in distribution %}
|
||||
<div class="h-full transition-all duration-700 ease-in-out flex items-center justify-center relative group first:rounded-l-md last:rounded-r-md"
|
||||
style="width: {{ item.percentage }}%; background-color: {{ item.color }};" _="on click
|
||||
halt the event
|
||||
put '<div class=\'flex flex-col gap-1\'>
|
||||
<div class=\'flex items-center gap-2\'>
|
||||
<div class=\'w-3 h-3 rounded-full\' style=\'background-color: {{ item.color }}\'></div>
|
||||
<span class=\'font-black text-sm uppercase\'>{{ item.attribute_name }}</span>
|
||||
</div>
|
||||
<div class=\'text-2xl font-black text-gray-900\'>{{ item.percentage }}%</div>
|
||||
<div class=\'text-[10px] font-bold text-gray-400 uppercase tracking-wider\'>{{ item.count }} Sets Targeted</div>
|
||||
</div>' into #content-{{ breakdown_id }}
|
||||
remove .hidden from #popover-{{ breakdown_id }}"
|
||||
title="{{ item.attribute_name }}: {{ item.percentage }}%">
|
||||
|
||||
<!-- Labels (Name & Percentage grouped and centered) -->
|
||||
<div
|
||||
class="flex items-center justify-center gap-1.5 leading-none text-white pointer-events-none px-1 overflow-hidden">
|
||||
{% if item.percentage > 18 %}
|
||||
<span class="text-[9px] font-black uppercase tracking-tighter truncate drop-shadow-sm">{{
|
||||
item.attribute_name }}</span>
|
||||
<span class="text-[9px] font-bold opacity-90 whitespace-nowrap drop-shadow-sm">{{ item.percentage
|
||||
}}%</span>
|
||||
{% elif item.percentage > 8 %}
|
||||
<span class="text-[9px] font-black drop-shadow-sm">{{ item.percentage }}%</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<!-- Enhanced Hover State -->
|
||||
<div class="absolute inset-0 bg-white/15 opacity-0 group-hover:opacity-100 transition-opacity"></div>
|
||||
</div>
|
||||
<li><span class="workout-legend-label"><i style="background-color: {{ item.color }};" aria-hidden="true"></i>{{ item.attribute_name }}</span><span title="{{ item.count }} sets targeted">{{ item.percentage }}%</span></li>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</ul>
|
||||
{% else %}<p class="workout-subtle">No categorized sets yet.</p>{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
@@ -0,0 +1,14 @@
|
||||
{% set totals = namespace(reps=0) %}
|
||||
{% for top_set in top_sets %}{% set totals.reps = totals.reps + (top_set.repetitions or 0)|int %}{% endfor %}
|
||||
<section class="workout-card workout-summary" id="workout-summary"
|
||||
aria-label="Session summary" aria-live="polite"
|
||||
hx-get="{{ url_for('workout.get_workout_summary', person_id=person_id, workout_id=workout_id) }}"
|
||||
hx-trigger="topsetAdded from:body, topsetChanged from:body" hx-swap="outerHTML">
|
||||
<p class="workout-eyebrow">This session</p>
|
||||
<dl>
|
||||
<div><dt>Exercises</dt><dd>{{ top_sets|map(attribute='exercise_id')|unique|list|length }}</dd></div>
|
||||
<div><dt>Top sets</dt><dd>{{ top_sets|length }}</dd></div>
|
||||
<div><dt>Reps</dt><dd>{{ totals.reps }}</dd></div>
|
||||
</dl>
|
||||
<p class="workout-subtle">Based on your logged top sets.</p>
|
||||
</section>
|
||||
Reference in New Issue
Block a user