Integrate session note and tag editing and center progress controls

This commit is contained in:
Peter Stockings
2026-09-06 22:38:11 +10:00
parent d5e3e1d930
commit dc0f41cd27
12 changed files with 156 additions and 110 deletions
+2 -2
View File
@@ -15,10 +15,10 @@
<link href="/static/css/tailwind.css" rel="stylesheet">
<link href="/static/css/style.css" rel="stylesheet">
<link href="/static/css/workout.css?v=1" rel="stylesheet">
<link href="/static/css/workout.css?v=2" rel="stylesheet">
<script src="/static/js/htmx.min.js" defer></script>
<script src="/static/js/hyperscript.min.js" defer></script>
<link rel="stylesheet" href="/static/css/forecast.css?v=2">
<link rel="stylesheet" href="/static/css/forecast.css?v=3">
<script src="/static/js/forecast.js?v=2" defer></script>
</head>
+2 -2
View File
@@ -4,13 +4,13 @@
<div class="progress-heading">
<div class="progress-title">
<h4>{{ title }}</h4>
<div hx-get="{{ url_for('workout.get_topset_achievements', person_id=person_id, workout_id=latest_workout_id, topset_id=latest_topset_id) }}"
hx-trigger="load" hx-target="this" hx-swap="innerHTML"></div>
</div>
<button class="progress-action" title="Show Progress Graph"
hx-get="{{ url_for('get_exercise_progress_for_user', person_id=person_id, exercise_id=exercise_id, epoch=selected_epoch, forecast='true' if forecast_enabled else 'false', forecast_weeks=forecast_weeks, forecast_reps=forecast_target_reps, min_date=min_date, max_date=max_date) }}"
hx-target="#exercise-history-container" hx-swap="outerHTML">Graph</button>
</div>
<div class="progress-badges" aria-label="Progress achievements" hx-get="{{ url_for('workout.get_topset_achievements', person_id=person_id, workout_id=latest_workout_id, topset_id=latest_topset_id) }}"
hx-trigger="load" hx-target="this" hx-swap="innerHTML"></div>
{% if best_fit_formula and not forecast_enabled %}
<h2 class="text-xs font-semibold text-blue-200 mb-4 text-center">
{{ best_fit_formula.kg_per_week }} kg/week, {{ best_fit_formula.kg_per_month }} kg/month
+28
View File
@@ -0,0 +1,28 @@
<section id="workout-note-{{ workout_id }}" class="workout-card workout-note-card"
hx-target="this" hx-swap="outerHTML">
{% if is_edit|default(false) %}
<form class="workout-note-editor"
hx-put="{{ url_for('notes.update_workout_note', person_id=person_id, workout_id=workout_id, view='session') }}"
hx-disabled-elt="find button"
_="on htmx:responseError remove .hidden from #note-error-{{ workout_id }}">
<label for="session-note-input-{{ workout_id }}">Session notes</label>
<textarea id="session-note-input-{{ workout_id }}" name="note" rows="5"
placeholder="Cues, energy, how it felt…"
_="init call me.focus() end">{{ note or '' }}</textarea>
<p id="note-error-{{ workout_id }}" class="workout-editor-error hidden" role="alert">Could not save your note. Please try again.</p>
<div class="workout-editor-actions">
<button type="button" class="workout-button"
hx-get="{{ url_for('notes.get_workout_note', person_id=person_id, workout_id=workout_id, view='session') }}">Cancel</button>
<button type="submit" class="workout-add-button">Save note</button>
</div>
</form>
{% else %}
<button type="button" class="workout-note-trigger"
hx-get="{{ url_for('notes.get_workout_note_edit_form', person_id=person_id, workout_id=workout_id, view='session') }}"
aria-label="{{ 'Edit session notes' if note else 'Add session notes' }}">
<span class="workout-inline-heading">Session notes <span class="workout-inline-action">{{ 'Edit' if note else '+ Add' }}</span></span>
{% if note %}<span class="workout-note-preview">{% for line in note.split('\n') %}{{ line }}{% if not loop.last %}<br>{% endif %}{% endfor %}</span>
{% else %}<span class="workout-subtle">Cues, energy, how it felt</span>{% endif %}
</button>
{% endif %}
</section>
+2 -2
View File
@@ -64,8 +64,6 @@ M{% for value, position in lower_points %}{{ (((position * vb_width) + margin) |
<div class="progress-heading">
<div class="progress-title">
<h4>{{ title }}</h4>
<div hx-get="{{ url_for('workout.get_topset_achievements', person_id=person_id, workout_id=latest_workout_id, topset_id=latest_topset_id) }}"
hx-trigger="load" hx-target="this" hx-swap="innerHTML"></div>
</div>
<div class="progress-actions">
<button class="progress-action" title="History and forecast tables"
@@ -78,6 +76,8 @@ M{% for value, position in lower_points %}{{ (((position * vb_width) + margin) |
hx-target="#svg-plot-{{ unique_id }}" hx-swap="outerHTML">Forecast {{ 'on' if forecast_enabled else 'off' }}</button>
</div>
</div>
<div class="progress-badges" aria-label="Progress achievements" hx-get="{{ url_for('workout.get_topset_achievements', person_id=person_id, workout_id=latest_workout_id, topset_id=latest_topset_id) }}"
hx-trigger="load" hx-target="this" hx-swap="innerHTML"></div>
<h2 class="text-xs font-semibold text-blue-200 mb-1 text-center">
{% if best_fit_formula and not forecast_enabled %}
{{ best_fit_formula.kg_per_week }} kg/week, {{ best_fit_formula.kg_per_month }} kg/month
@@ -0,0 +1,28 @@
<div id="workout-tag-editor-{{ workout_id }}" class="workout-tag-editor">
<form hx-post="{{ url_for('tags.add_tag_to_workout', workout_id=workout_id, view='session') }}"
hx-trigger="change" hx-target="#workout-tag-editor-{{ workout_id }}" hx-swap="outerHTML"
hx-disabled-elt="#workout-tag-editor-{{ workout_id }} input, #workout-tag-editor-{{ workout_id }} button"
_="on htmx:responseError remove .hidden from #tag-error-{{ workout_id }}">
<fieldset>
<legend class="workout-subtle">Select tags for this session</legend>
<div class="workout-tag-choices">
{% for tag in tags %}
<label class="workout-tag-choice">
<input id="workout-{{ workout_id }}-tag-{{ tag.tag_id }}" type="checkbox" name="tag_id" value="{{ tag.tag_id }}" {% if tag.is_selected %}checked{% endif %}>
<span>{{ tag.tag_name }}</span>
</label>
{% else %}<p class="workout-subtle">Create your first tag below.</p>{% endfor %}
</div>
</fieldset>
</form>
<form class="workout-tag-create"
hx-post="{{ url_for('tags.create_new_tag_for_workout', workout_id=workout_id, view='session') }}"
hx-target="#workout-tag-editor-{{ workout_id }}" hx-swap="outerHTML"
hx-disabled-elt="#workout-tag-editor-{{ workout_id }} input, #workout-tag-editor-{{ workout_id }} button"
_="on htmx:responseError remove .hidden from #tag-error-{{ workout_id }}">
<label for="new-workout-tag-{{ workout_id }}" class="sr-only">New tag name</label>
<input id="new-workout-tag-{{ workout_id }}" type="text" name="tag_name" placeholder="New tag…" required>
<button type="submit" class="workout-button">Add tag</button>
</form>
<p id="tag-error-{{ workout_id }}" class="workout-editor-error hidden" role="alert">Could not save tags. Please try again.</p>
</div>
+4 -73
View File
@@ -1,73 +1,4 @@
<div class="flex">
<div id="tag-wrapper-w-{{ workout_id }}">
{{ render_partial('partials/workout_tags_list.html', tags=tags)}}
</div>
<span id="tag-form-show-w-{{ workout_id }}" class="cursor-pointer" _="on click
remove .hidden from #tag-form-w-{{ workout_id }}
then remove .hidden from #tag-form-hide-w-{{ workout_id }}
then add .hidden">
<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" data-darkreader-inline-stroke="" style="--darkreader-inline-stroke: currentColor;">
<path stroke-linecap="round" stroke-linejoin="round" d="M12 4.5v15m7.5-7.5h-15"></path>
</svg>
</span>
<span id="tag-form-hide-w-{{ workout_id }}" _="on click
add .hidden to #tag-form-w-{{ workout_id }}
then remove .hidden from #tag-form-show-w-{{ workout_id }}
then add .hidden" class="cursor-pointer hidden">
<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="M19.5 12h-15" />
</svg>
</span>
</div>
<div id="tag-form-w-{{ workout_id }}" class="hidden pt-2 pb-3">
<span class="text-base font-normal text-gray-500">Add tag to workout</span>
<div class="flex pt-2 flex-col sm:flex-row pb-2">
<div class="w-full md:w-1/2 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">
Tag
</label>
<div class="relative">
<div class="w-full">
{{ render_partial('partials/custom_select.html',
name='tag_id',
options=tags,
multiple=true,
search=true,
placeholder='Select tags',
hx_post=url_for('tags.add_tag_to_workout', workout_id=workout_id),
hx_target='#tag-wrapper-w-' + workout_id|string)
}}
</div>
</div>
</div>
<div class="w-full md:w-1/2 px-3 mb-6 md:mb-0">
<label class="block uppercase tracking-wide text-gray-700 text-xs font-bold mb-2">
New tag name
</label>
<div class="flex">
<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"
type="text" name="tag_name">
<button type="submit" hx-post="{{ url_for('tags.create_new_tag_for_workout', workout_id=workout_id) }}"
hx-include="[name='tag_name']" hx-target="#tag-wrapper-w-{{ workout_id }}"
class="p-2.5 ml-2 text-sm font-medium text-white bg-blue-700 rounded-lg border border-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800">
<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" data-darkreader-inline-stroke=""
style="--darkreader-inline-stroke: currentColor;">
<path stroke-linecap="round" stroke-linejoin="round" d="M12 4.5v15m7.5-7.5h-15"></path>
</svg>
<span class="sr-only">Search</span>
</button>
</div>
</div>
</div>
</div>
<section class="workout-card workout-tags-card" aria-labelledby="workout-tags-heading-{{ workout_id }}">
<h2 id="workout-tags-heading-{{ workout_id }}">Tags</h2>
{{ render_partial('partials/workout_tag_editor.html', person_id=person_id, workout_id=workout_id, tags=tags) }}
</section>
+5 -20
View File
@@ -46,14 +46,9 @@
<h1>{{ person_name }}<span> / Workout</span></h1>
{{ render_partial('partials/start_date.html', person_id=person_id, workout_id=workout_id, start_date=start_date) }}
</div>
<details class="workout-menu">
<summary class="workout-button">Session options <span aria-hidden="true">&middot;&middot;&middot;</span></summary>
<div class="workout-menu-content">
<button type="button" class="workout-delete"
hx-get="{{ url_for('workout.delete_workout', person_id=person_id, workout_id=workout_id) }}"
hx-confirm="Are you sure you wish to delete this workout?" hx-push-url="true" hx-target="#container">Delete workout</button>
</div>
</details>
<button type="button" class="workout-button workout-delete"
hx-get="{{ url_for('workout.delete_workout', person_id=person_id, workout_id=workout_id) }}"
hx-confirm="Are you sure you wish to delete this workout?" hx-push-url="true" hx-target="#container">Delete workout</button>
</header>
<div class="workout-layout">
@@ -96,18 +91,8 @@
</details>
</div>
</details>
<details class="workout-card workout-disclosure" {% if note %}open{% endif %}>
<summary>Session notes <span class="workout-subtle">Cues, energy, how it felt</span></summary>
<div class="workout-disclosure-body">
{{ render_partial('partials/workout_note.html', person_id=person_id, workout_id=workout_id, note=note or '') }}
</div>
</details>
<details class="workout-card workout-disclosure">
<summary>Tags <span class="workout-subtle">Organize this session</span></summary>
<div class="workout-disclosure-body">
{{ render_partial('partials/workout_tags.html', person_id=person_id, workout_id=workout_id, tags=tags) }}
</div>
</details>
{{ render_partial('partials/session_note.html', person_id=person_id, workout_id=workout_id, note=note or '') }}
{{ render_partial('partials/workout_tags.html', person_id=person_id, workout_id=workout_id, tags=tags) }}
</aside>
</div>
</div>