Simplify forecast panel and extend horizon

This commit is contained in:
Peter Stockings
2026-09-06 14:50:51 +10:00
parent d26da9a066
commit 0dea94fec0
5 changed files with 25 additions and 31 deletions
+1 -1
View File
@@ -23,7 +23,7 @@
</button>
</div>
</div>
{% if best_fit_formula %}
{% 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
</h2>
+18 -27
View File
@@ -34,6 +34,7 @@ M{% for value, position in lower_points %}{{ (((position * vb_width) + margin) |
{% macro forecast_circles(data_points, details, color) %}
{% for value, position in data_points %}
{% if loop.last %}
{% set x = (position * vb_width)+margin %}
{% set y = (vb_height - value)+margin %}
<circle cx="{{ x | int }}" cy="{{ y | int }}" r="1.6" fill="{{ color }}" stroke="{{ color }}"
@@ -43,6 +44,7 @@ M{% for value, position in lower_points %}{{ (((position * vb_width) + margin) |
data-e1rm="{{ details[loop.index0].e1rm }}"
data-topset="{{ details[loop.index0].top_set }}"
data-range="{{ details[loop.index0].range }}"></circle>
{% endif %}
{% endfor %}
{% endmacro %}
@@ -102,9 +104,9 @@ M{% for value, position in lower_points %}{{ (((position * vb_width) + margin) |
</div>
</div>
<h2 class="text-xs font-semibold text-blue-200 mb-1 text-center">
{% if best_fit_formula %}
{{ best_fit_formula.kg_per_week }} kg/week, {{ best_fit_formula.kg_per_month }} kg/month
{% endif %}
{% if best_fit_formula and not forecast_enabled %}
{{ best_fit_formula.kg_per_week }} kg/week, {{ best_fit_formula.kg_per_month }} kg/month
{% endif %}
</h2>
<div class="inline-flex rounded-md shadow-sm w-full items-center justify-center mb-1">
{% for epoch in epochs %}
@@ -127,43 +129,32 @@ M{% for value, position in lower_points %}{{ (((position * vb_width) + margin) |
{% endfor %}
</div>
{% if forecast_enabled %}
<div class="mx-2 mb-3 rounded-xl border {% if forecast.available %}border-emerald-100 bg-emerald-50/70{% else %}border-amber-100 bg-amber-50/70{% endif %} p-3 text-left">
<div class="mx-2 mb-3 rounded-xl border {% if forecast.available %}border-emerald-100 bg-emerald-50/70{% else %}border-amber-100 bg-amber-50/70{% endif %} p-2.5 text-left">
{% if forecast.available %}
<div class="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-2">
<div class="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-1.5">
<div>
<div class="text-xs font-semibold uppercase tracking-wide text-emerald-800">E1RM projection</div>
<div class="text-sm text-gray-700 mt-0.5">{{ forecast.last_value }}kg now <span class="text-gray-400">→</span> <span class="font-bold text-emerald-800">{{ forecast.projected_value }}kg</span> in {{ forecast.horizon_weeks }} weeks</div>
<div class="text-xs font-semibold uppercase tracking-wide text-emerald-800">Forecast · {{ forecast.horizon_weeks }} weeks</div>
<div class="text-lg font-bold text-gray-800 mt-0.5">{{ forecast.projected_value }}kg <span class="text-xs font-normal text-gray-500">projected E1RM</span></div>
</div>
<div class="text-left sm:text-right">
<div class="text-sm font-semibold {% if forecast.weekly_change >= 0 %}text-emerald-700{% else %}text-rose-700{% endif %}">{% if forecast.weekly_change >= 0 %}+{% endif %}{{ forecast.weekly_change }}kg/week</div>
<div class="text-sm font-semibold {% if forecast.projected_change >= 0 %}text-emerald-700{% else %}text-rose-700{% endif %}">{% if forecast.projected_change >= 0 %}+{% endif %}{{ forecast.projected_change }}kg total</div>
<div class="text-xs text-gray-500">{{ forecast.confidence }} confidence · {{ forecast.observed_points }} sessions</div>
</div>
</div>
{% if forecast.selected_top_set %}
<div class="mt-3 flex flex-col sm:flex-row sm:items-center sm:justify-between gap-2 rounded-lg border border-emerald-100 bg-white/80 px-3 py-2.5">
<div class="flex items-center justify-between gap-2">
<div>
<div class="text-xs font-semibold uppercase tracking-wide text-gray-500">Projected top set</div>
<div class="text-lg font-bold text-gray-800">{{ forecast.selected_top_set.weight_display }}kg × {{ forecast.selected_top_set.repetitions }} reps</div>
</div>
<div class="text-right text-xs text-gray-500">
<div>Model range</div>
<div class="font-semibold text-emerald-700">{{ forecast.selected_top_set.lower_weight_display }}–{{ forecast.selected_top_set.upper_weight_display }}kg</div>
</div>
</div>
<div class="text-[11px] text-gray-500 sm:text-right">
Full rep-to-weight forecast in the table view.<br>
Based on {{ forecast.observed_rep_min }}–{{ forecast.observed_rep_max }} reps in view.
</div>
<div class="mt-2 flex flex-wrap items-center gap-x-2 gap-y-1 rounded-lg bg-white/80 px-2.5 py-2 text-sm">
<span class="text-xs font-semibold uppercase tracking-wide text-gray-500">Top-set guide</span>
<span class="font-bold text-gray-800">{{ forecast.selected_top_set.weight_display }}kg × {{ forecast.selected_top_set.repetitions }}</span>
<span class="text-xs text-gray-500">at the selected rep target · details in History</span>
</div>
{% endif %}
<div class="flex flex-wrap items-center gap-2 mt-3">
<div class="flex flex-wrap items-center gap-1.5 mt-2.5">
<span class="text-xs text-gray-500 mr-1">Horizon</span>
{% for weeks in [4, 8, 12, 26] %}
<button class="px-2.5 py-1 rounded-md text-xs font-medium {% if forecast_weeks == weeks %}bg-emerald-700 text-white{% else %}bg-white text-gray-600 border border-emerald-200 hover:bg-emerald-100{% endif %}"
{% for weeks, label in forecast_horizons %}
<button class="px-2 py-0.5 rounded-md text-xs font-medium {% if forecast_weeks == weeks %}bg-emerald-700 text-white{% else %}bg-white text-gray-600 border border-emerald-200 hover:bg-emerald-100{% endif %}"
hx-get="{{ url_for('get_exercise_progress_for_user', person_id=person_id, exercise_id=exercise_id, epoch=selected_epoch, forecast='true', forecast_weeks=weeks, forecast_reps=forecast_target_reps) }}"
hx-include="#svg-plot-{{ unique_id }} [name='min_date'], #svg-plot-{{ unique_id }} [name='max_date'], #svg-plot-{{ unique_id }} [name='degree']"
hx-target="#svg-plot-{{ unique_id }}" hx-swap="outerHTML">{{ weeks }}w</button>
hx-target="#svg-plot-{{ unique_id }}" hx-swap="outerHTML">{{ label }}</button>
{% endfor %}
</div>
{% else %}