Skip to content

Commit 0d51770

Browse files
committed
fix(webui): nav badge safe on pages without PendingProposalCount
Use getPendingCount reflection helper instead of direct field access. Pages without PendingProposalCount no longer panic on template render. Add PendingProposalCount to proposalListView and proposalDetailView.
1 parent 2a49830 commit 0d51770

3 files changed

Lines changed: 50 additions & 11 deletions

File tree

internal/webui/embed.go

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"html/template"
88
"io/fs"
99
"net/http"
10+
"reflect"
1011
"strings"
1112
"time"
1213

@@ -136,6 +137,36 @@ func parseTemplates(extraFuncs ...template.FuncMap) (map[string]*template.Templa
136137
}
137138
return *p
138139
},
140+
"pendingCount": func(n int) int {
141+
if n < 0 {
142+
return 0
143+
}
144+
return n
145+
},
146+
"getPendingCount": func(data interface{}) int {
147+
// Use reflection to safely get PendingProposalCount from any struct.
148+
// Returns 0 if field missing or not an int.
149+
v := reflect.ValueOf(data)
150+
if v.Kind() != reflect.Ptr {
151+
return 0
152+
}
153+
v = v.Elem()
154+
if v.Kind() != reflect.Struct {
155+
return 0
156+
}
157+
f := v.FieldByName("PendingProposalCount")
158+
if !f.IsValid() {
159+
return 0
160+
}
161+
if f.Kind() != reflect.Int {
162+
return 0
163+
}
164+
n := int(f.Int())
165+
if n < 0 {
166+
return 0
167+
}
168+
return n
169+
},
139170
"subtreeIsLarger": func(r RunSummary) bool {
140171
return r.SubtreeTokens > int64(r.TotalTokens)
141172
},

internal/webui/handlers_proposals.go

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,11 @@ import (
1515

1616
// proposalListView is the template payload for the proposals list page.
1717
type proposalListView struct {
18-
ActivePage string
19-
Filter string
20-
Counts map[string]int
21-
Proposals []proposalRow
18+
ActivePage string
19+
Filter string
20+
Counts map[string]int
21+
Proposals []proposalRow
22+
PendingProposalCount int
2223
}
2324

2425
// proposalRow is one entry in the proposals list table.
@@ -36,12 +37,13 @@ type proposalRow struct {
3637

3738
// proposalDetailView is the template payload for the proposal detail page.
3839
type proposalDetailView struct {
39-
ActivePage string
40-
Proposal proposalRow
41-
DiffLines []diffLine
42-
DiffMissing bool
43-
DiffError string
44-
SignalSummary string
40+
ActivePage string
41+
Proposal proposalRow
42+
DiffLines []diffLine
43+
DiffMissing bool
44+
DiffError string
45+
SignalSummary string
46+
PendingProposalCount int
4547
}
4648

4749
// diffLine is one row of a unified diff with a class for line type.
@@ -108,6 +110,9 @@ func (s *Server) handleProposalsPage(w http.ResponseWriter, r *http.Request) {
108110
Counts: counts,
109111
Proposals: rows,
110112
}
113+
if recs, err := store.ListProposalsByStatus(state.ProposalProposed, 0); err == nil {
114+
view.PendingProposalCount = len(recs)
115+
}
111116

112117
w.Header().Set("Content-Type", "text/html; charset=utf-8")
113118
tmpl := s.assets.templates["templates/proposals/list.html"]
@@ -148,6 +153,9 @@ func (s *Server) handleProposalDetailPage(w http.ResponseWriter, r *http.Request
148153
Proposal: recordToRow(*rec),
149154
SignalSummary: rec.SignalSummary,
150155
}
156+
if recs, err := store.ListProposalsByStatus(state.ProposalProposed, 0); err == nil {
157+
view.PendingProposalCount = len(recs)
158+
}
151159

152160
if rec.DiffPath == "" {
153161
view.DiffMissing = true

internal/webui/templates/partials/nav_main.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
</a>
2727
<a href="/proposals"{{if eq .ActivePage "proposals"}} class="active"{{end}}>
2828
<svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/><line x1="16" y1="13" x2="8" y2="13"/><line x1="16" y1="17" x2="8" y2="17"/></svg>
29-
Proposals{{if gt .PendingProposalCount 0}}<span style="font-size: 10px; padding: 1px 6px; background: #d29922; color: #0d1117; border-radius: 10px; margin-left: 4px;">{{.PendingProposalCount}}</span>{{end}}
29+
Proposals{{if gt (getPendingCount .) 0}}<span style="font-size: 10px; padding: 1px 6px; background: #d29922; color: #0d1117; border-radius: 10px; margin-left: 4px;">{{getPendingCount .}}</span>{{end}}
3030
</a>
3131
<a href="/issues"{{if eq .ActivePage "issues"}} class="active"{{end}}>
3232
<svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><line x1="12" y1="8" x2="12" y2="12"/><line x1="12" y1="16" x2="12.01" y2="16"/></svg>

0 commit comments

Comments
 (0)