feat(views): Phase 5j slice G — show_count badges + icon registry
Per m's v1 picks (2026-05-29):
- Q6 (icon picker): yes, with curated keys + SVG registry.
- Q8 (show_count badge): yes, opt-in checkbox + sidebar badge.
Icon registry (web/icons.go):
- 7 curated keys: folder (default), clock, star, tag, inbox, box,
file-text. Each maps to a Feather-style 24x24 SVG matching the rest
of the projax sidebar aesthetic. Returns template.HTML so layout.tmpl
emits markup verbatim. Unknown / nil keys fall back to folder.
- RenderViewIcon(*string) is template-callable; IconRegistryKeys()
feeds the editor's <select>.
- Funcs map in web/server.go gains a "renderIcon" entry.
show_count badge (web/server.go + web/templates/layout.tmpl):
- render() now computes per-saved-view counts when ANY view in the
list has ShowCount=true. One ListAll per render, shared across all
show-count views; for each opted-in view the persisted filter_json
is decoded into a TreeFilter and matched against every item.
- Counts pass to the template as UserViewCounts (slug → count). The
template renders {{index $counts $slug}} inside a nav-badge span
next to the view's name.
Template updates:
- layout.tmpl: replaces the diamond-glyph placeholder with
{{renderIcon .Icon}}; show_count views emit a .nav-badge next to
their name.
- view_editor.tmpl: icon <select> now sourced from IconKeys data
(the editor handler passes IconRegistryKeys()).
CSS additions:
- nav-badge: muted-color, surface-background, pill-shaped, pushed to
the right via margin-left:auto so the badge aligns with the row's
end regardless of name length.
- nav-item-user-view.active .nav-badge: switches to accent border +
color so the active row's badge stays legible.
Tests:
- TestSidebarShowCountBadge — seeds show_count=true view, asserts
.nav-badge markup in the sidebar.
- TestSidebarIconRenders — seeds icon=star view, asserts the
distinctive star polygon path lands in the sidebar SVG.
Drag-reorder UI stays parked (m's Q7=(b) v2). sort_order column is
server-assigned MAX+1 on create; the column was wired in slice A and
ReorderViews is ready for slice G's followup.
This commit is contained in:
@@ -132,6 +132,10 @@ func New(s *store.Store, logger *slog.Logger) (*Server, error) {
|
||||
"addF": func(a, b any) float64 { return toFloat(a) + toFloat(b) },
|
||||
"subF": func(a, b any) float64 { return toFloat(a) - toFloat(b) },
|
||||
"mulF": func(a, b any) float64 { return toFloat(a) * toFloat(b) },
|
||||
// Phase 5j slice G — sidebar icon registry. layout.tmpl calls
|
||||
// `renderIcon .View.Icon` to emit the matching SVG, falling back to
|
||||
// the folder default for nil / unknown keys.
|
||||
"renderIcon": RenderViewIcon,
|
||||
"tagToggleURL": func(active []string, tag string, isActive bool) string {
|
||||
next := []string{}
|
||||
if isActive {
|
||||
@@ -1017,6 +1021,38 @@ func (s *Server) render(w http.ResponseWriter, r *http.Request, name string, dat
|
||||
if _, set := data["UserViews"]; !set && name != "login" && s.Store != nil {
|
||||
if uv, err := s.Store.ListViews(r.Context()); err == nil {
|
||||
data["UserViews"] = uv
|
||||
// Phase 5j slice G — show_count badges. For every view with
|
||||
// ShowCount=true, run its persisted filter against ListAll and
|
||||
// pass a slug→count map to the template. Caching is one
|
||||
// ListAll per render shared across all show-count views.
|
||||
counts := map[string]int{}
|
||||
needsCount := false
|
||||
for _, v := range uv {
|
||||
if v.ShowCount {
|
||||
needsCount = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if needsCount {
|
||||
items, err := s.Store.ListAll(r.Context())
|
||||
if err == nil {
|
||||
linkKinds, _ := s.linkKindsByItem(r.Context())
|
||||
for _, v := range uv {
|
||||
if !v.ShowCount {
|
||||
continue
|
||||
}
|
||||
f, _, _ := decodeViewSpec(v.FilterJSON)
|
||||
n := 0
|
||||
for _, it := range items {
|
||||
if f.Matches(it, linkKinds[it.ID]) {
|
||||
n++
|
||||
}
|
||||
}
|
||||
counts[v.Slug] = n
|
||||
}
|
||||
}
|
||||
}
|
||||
data["UserViewCounts"] = counts
|
||||
}
|
||||
}
|
||||
entry := "layout"
|
||||
|
||||
Reference in New Issue
Block a user