Add training tracking: exercises with instructions, one-tap session log, consistency overview

Exercises (name + how-to note) are a new synced collection with the same
LWW/tombstone contract as events, served by POST /api/exercises/sync.
Training sessions are ordinary events (type "training") referencing an
exercise by id, so they ride the existing event sync unchanged.

The Training panel lists each exercise with last-trained / this-week /
streak stats, expandable instructions, and a one-tap Log button with the
usual undo/add-note snackbar. An exercise-by-day heatmap shows the last
14 days of consistency, and history and the daily overview count
training sessions like any other event.
This commit is contained in:
Alexander Heldt
2026-07-12 16:01:41 +00:00
parent 561b98b64f
commit 2e817a086d
6 changed files with 573 additions and 40 deletions
+33
View File
@@ -90,6 +90,18 @@
</div>
</section>
<section class="training" data-panel="training">
<h2>Training</h2>
<ul id="training-list" class="training-list"></ul>
<p id="training-empty" class="empty">No exercises yet. Add one to start tracking training.</p>
<button type="button" id="exercise-add" class="ghost training-add">Add exercise</button>
<div class="chart training-chart" id="training-chart-wrap" hidden>
<div class="chart-title">Consistency (last 14 days)</div>
<svg id="chart-training" class="chart-svg" viewBox="0 0 320 60" role="img" aria-label="Training sessions per exercise per day over the last 14 days"></svg>
<p class="muted-note">Darker = more sessions that day. Tap a cell to open that day.</p>
</div>
</section>
<section class="day-bar">
<button type="button" id="day-prev" class="ghost" aria-label="Previous day"></button>
<input type="date" id="day-picker" />
@@ -120,6 +132,10 @@
<div class="stat-label">Poos</div>
<div class="stat-value" id="stat-poos">0</div>
</div>
<div class="stat">
<div class="stat-label">Training</div>
<div class="stat-value" id="stat-training">0</div>
</div>
</div>
<div class="lasts">
@@ -252,6 +268,23 @@
</form>
</dialog>
<dialog id="exercise-dialog">
<form method="dialog" id="exercise-form">
<h3 id="exercise-title">Add exercise</h3>
<label>Name
<input type="text" id="exercise-name" placeholder="e.g. Sit" autocomplete="off" />
</label>
<label>How to do it
<textarea id="exercise-note" rows="5" placeholder="Reminder for how to train it, e.g. lure with a treat, mark the moment the butt touches the ground, reward"></textarea>
</label>
<menu>
<button value="delete" id="exercise-delete" class="danger" hidden>Delete</button>
<button value="cancel" class="ghost">Cancel</button>
<button value="save" id="exercise-save">Save</button>
</menu>
</form>
</dialog>
<dialog id="note-dialog">
<form method="dialog" id="note-form">
<h3 id="note-title">Add note</h3>