Items-list sparklines, retro CSS, pinned tooling, deploy docs

- Bulk-load recent price points per item and render a sparkline in
  the items list (new LoadRecentPriceHistory query avoids N+1).
- Add retro.css visual layer and refreshed login/items/layout styling.
- Swap the logo from webp to avif.
- Pin htmx/Chart.js/Tailwind/templ versions in the Makefile with
  vendor / tools / update-deps targets; README documents the
  dependency-bump flow and the hardened systemd deploy.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
prosolis
2026-05-15 19:10:56 -07:00
parent 0ec97afafb
commit ea3577a45e
17 changed files with 1174 additions and 343 deletions

View File

@@ -30,11 +30,22 @@ func (a *App) GetItems(w http.ResponseWriter, r *http.Request) {
}
}
cats, _ := a.Store.ListCategories(r.Context())
// Bulk-load recent price history so each row can render a sparkline
// without N+1 queries. 20 points is enough for a meaningful trend line
// at 80px wide and stays cheap on the largest realistic watchlists.
ids := make([]int64, 0, len(items))
for _, it := range items {
ids = append(ids, it.ID)
}
history, _ := a.Store.LoadRecentPriceHistory(r.Context(), ids, 20)
render(w, r, templates.Items(templates.ItemsData{
Page: a.page(r, "Items", "items"),
Items: items,
Categories: cats,
SelectedCategory: cat,
PriceHistory: history,
}))
}
@@ -387,7 +398,8 @@ func (a *App) PostToggleItem(w http.ResponseWriter, r *http.Request) {
return
}
a.Scheduler.SyncItem(*it)
render(w, r, templates.ItemRow(*it, a.Auth.CSRFToken(r.Context())))
hist, _ := a.Store.LoadRecentPriceHistory(r.Context(), []int64{id}, 20)
render(w, r, templates.ItemRow(*it, a.Auth.CSRFToken(r.Context()), hist[id]))
}
func (a *App) PostDeleteItem(w http.ResponseWriter, r *http.Request) {