Use HTMX fragements on the fitness dashboard

This commit is contained in:
2026-10-02 17:42:29 +02:00
parent aa90a04f91
commit 7749d7c237
5 changed files with 139 additions and 24 deletions
+61 -2
View File
@@ -34,13 +34,42 @@ func (f *fitnessHandler) handle(root, fsPath, urlPath string, r *http.Request) *
if s == nil || s.Type != "fitness" {
return nil
}
q := r.URL.Query()
weightView, weeklyView := fitnessViews(r.URL.Query())
return &specialPage{
Content: renderFitnessDashboard(fsPath, chartViewFrom(q, "weight", "3m"), chartViewFrom(q, "weekly", "1y")),
Content: renderFitnessDashboard(fsPath, weightView, weeklyView),
SuppressTOC: true,
}
}
// serveFragment answers a chart's controls (htmx GET ?chart=<param> with
// every chart's view state) with just that chart, and points the address bar
// at the canonical dashboard URL so the view stays bookmarkable.
func (f *fitnessHandler) serveFragment(w http.ResponseWriter, r *http.Request, root, fsPath, urlPath string) bool {
q := r.URL.Query()
param := q.Get("chart")
if param == "" {
return false
}
if s := readPageSettings(fsPath); s == nil || s.Type != "fitness" {
return false
}
weightView, weeklyView := fitnessViews(q)
dash := buildFitnessDash(fsPath, weightView, weeklyView, time.Now())
for _, c := range dash.Charts {
if c.Param != param {
continue
}
w.Header().Set("Content-Type", "text/html; charset=utf-8")
w.Header().Set("HX-Replace-Url", fitnessURL(urlPath, weightView, weeklyView))
if err := fitnessTmpl.ExecuteTemplate(w, "fitnessChart", c); err != nil {
log.Printf("fitness template: %v", err)
}
return true
}
http.Error(w, "Unknown chart: "+param, http.StatusNotFound)
return true
}
// === Time ranges ===
type fitnessRange struct {
@@ -79,6 +108,36 @@ func chartViewFrom(q url.Values, param, fallback string) fitnessView {
}
}
// Default ranges of the two charts.
const (
weightDefaultRange = "3m"
weeklyDefaultRange = "1y"
)
func fitnessViews(q url.Values) (weight, weekly fitnessView) {
return chartViewFrom(q, "weight", weightDefaultRange), chartViewFrom(q, "weekly", weeklyDefaultRange)
}
// fitnessURL is the dashboard URL for the given views; defaults are left out.
func fitnessURL(urlPath string, weight, weekly fitnessView) string {
q := url.Values{}
add := func(param, fallback string, v fitnessView) {
if v.Range != fallback {
q.Set(param, v.Range)
}
if v.Absolute {
q.Set(param+"-absolute", "1")
}
}
add("weight", weightDefaultRange, weight)
add("weekly", weeklyDefaultRange, weekly)
target := pageURL(urlPath)
if len(q) > 0 {
target += "?" + q.Encode()
}
return target
}
func rangeMonths(v string) int {
for _, r := range fitnessRanges {
if r.Value == v {