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.
50 lines
2.0 KiB
Cheetah
50 lines
2.0 KiB
Cheetah
{{define "content"}}
|
|
<h1>{{if .View}}Edit {{.View.Name}}{{else}}New view{{end}}</h1>
|
|
<p class="muted"><a href="/views">← back to views</a></p>
|
|
|
|
<form class="view-editor"
|
|
method="post"
|
|
action="{{if .View}}/views/{{.View.Slug}}{{else}}/views{{end}}">
|
|
<label>Name <input type="text" name="name" required maxlength="80" value="{{if .View}}{{.View.Name}}{{end}}"></label>
|
|
<label>Slug
|
|
<input type="text" name="slug" required maxlength="63"
|
|
pattern="^[a-z0-9][a-z0-9-]{0,62}$"
|
|
value="{{if .View}}{{.View.Slug}}{{end}}">
|
|
<small class="muted">lowercase letters, digits, dashes. No reserved system slugs.</small>
|
|
</label>
|
|
<label>Icon
|
|
<select name="icon">
|
|
{{$cur := ""}}
|
|
{{if and .View .View.Icon}}{{$cur = deref .View.Icon}}{{end}}
|
|
{{range .IconKeys}}
|
|
<option value="{{.}}"{{if eq . $cur}} selected{{end}}>{{.}}</option>
|
|
{{end}}
|
|
</select>
|
|
</label>
|
|
<fieldset class="view-type-radios">
|
|
<legend>View type</legend>
|
|
{{range .ViewTypes}}
|
|
<label><input type="radio" name="view_type" value="{{.}}" {{if eq . $.CurrentVT}}checked{{end}}> {{.}}</label>
|
|
{{end}}
|
|
</fieldset>
|
|
<label>Group by
|
|
<select name="group_by">
|
|
{{range .GroupByOptions}}<option value="{{.}}">{{if eq . ""}}—{{else}}{{.}}{{end}}</option>{{end}}
|
|
</select>
|
|
</label>
|
|
<label>Sort field <input type="text" name="sort_field" placeholder="title / updated_at" maxlength="40"></label>
|
|
<label>Sort dir
|
|
<select name="sort_dir">
|
|
{{range .SortDirOptions}}<option value="{{.}}">{{if eq . ""}}—{{else}}{{.}}{{end}}</option>{{end}}
|
|
</select>
|
|
</label>
|
|
<label><input type="checkbox" name="show_count" value="1"
|
|
{{if and .View .View.ShowCount}}checked{{end}}> Show row-count badge in sidebar</label>
|
|
<label>Filter (URL query form)
|
|
<input type="text" name="filter_query" placeholder="tag=work&mgmt=mai" value="{{.FilterQuery}}">
|
|
</label>
|
|
<button type="submit">{{if .View}}Save changes{{else}}Create view{{end}}</button>
|
|
<a class="muted" href="/views">cancel</a>
|
|
</form>
|
|
{{end}}
|