Skip to content

Commit 2838fbb

Browse files
committed
internal/relui: simplify rendering of homepage
This refactors the homepage rendering to use a nested template for each task row. This will help simplify the template as we add more complex layout to the outputs of workflows and tasks. Updates golang/go#51797 Updates golang/go#40279 For golang/go#53382 Change-Id: I85a86b82bdc79c7fb4e837d884af922c7028295d Reviewed-on: https://go-review.googlesource.com/c/build/+/412176 Reviewed-by: Dmitri Shuralyov <[email protected]> Reviewed-by: Dmitri Shuralyov <[email protected]> TryBot-Result: Gopher Robot <[email protected]> Run-TryBot: Alex Rakoczy <[email protected]>
1 parent f7e019d commit 2838fbb

File tree

5 files changed

+145
-106
lines changed

5 files changed

+145
-106
lines changed

internal/relui/templates/home.html

Lines changed: 8 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,18 @@
33
Use of this source code is governed by a BSD-style
44
license that can be found in the LICENSE file.
55
-->
6+
{{template "layout" .}}
7+
68
{{define "content"}}
79
<section class="Workflows">
810
<div class="Workflows-header">
911
<h2>Workflows</h2>
1012
<a href="{{baseLink "/workflows/new"}}" class="Button">New</a>
1113
</div>
1214
<ul class="WorkflowList">
13-
{{range $workflow := .Workflows}}
15+
{{range $wfid := .WorkflowIDs}}
16+
{{$detail := index $.WorkflowDetails $wfid}}
17+
{{$workflow := $detail.Workflow}}
1418
<li class="WorkflowList-item">
1519
<h3 class="WorkflowList-title">
1620
{{$workflow.Name.String}}
@@ -19,8 +23,8 @@ <h3 class="WorkflowList-title">
1923
</span>
2024
{{if not (or $workflow.Finished $workflow.Error)}}
2125
<div class="WorkflowList-titleStop">
22-
<form action="{{baseLink (printf "/workflows/%s/stop" $workflow.ID) }}" method="post">
23-
<input type="hidden" id="workflow.id" name="workflow.id" value="{{$workflow.ID}}" />
26+
<form action="{{baseLink (printf "/workflows/%s/stop" $wfid)}}" method="post">
27+
<input type="hidden" id="workflow.id" name="workflow.id" value="{{$wfid}}" />
2428
<input name="workflow.stop" class="Button Button--red" type="submit" value="STOP" onclick="return this.form.reportValidity() && confirm('This will stop the workflow and all in-flight tasks.\n\nAre you sure you want to proceed?')" />
2529
</form>
2630
</div>
@@ -60,87 +64,7 @@ <h3 class="WorkflowList-title">
6064
</tbody>
6165
</table>
6266
<h4 class="WorkflowList-sectionTitle">Tasks</h4>
63-
<table class="TaskList">
64-
<thead>
65-
<tr class="TaskList-item TaskList-itemHeader">
66-
<th class="TaskList-itemHeaderCol TaskList-itemExpand"></th>
67-
<th class="TaskList-itemHeaderCol TaskList-itemState">State</th>
68-
<th class="TaskList-itemHeaderCol TaskList-itemName">Name</th>
69-
<th class="TaskList-itemHeaderCol TaskList-itemStarted">Started</th>
70-
<th class="TaskList-itemHeaderCol TaskList-itemUpdated">Updated</th>
71-
<th class="TaskList-itemHeaderCol TaskList-itemResult">Result</th>
72-
<th class="TaskList-itemHeaderCol TaskList-itemActions">Actions</th>
73-
</tr>
74-
</thead>
75-
{{$tasks := index $.WorkflowTasks $workflow.ID}}
76-
{{range $task := $tasks}}
77-
<tbody>
78-
<tr class="TaskList-item TaskList-itemSummary TaskList-expandableItem">
79-
<td class="TaskList-itemCol TaskList-itemExpand">
80-
<span class="TaskList-itemExpandClosed">
81-
<img class="TaskList-itemExpandControl" alt="unfold more" src="{{baseLink "/static/images/chevron_right_black_24dp.svg"}}" />
82-
</span>
83-
<span class="TaskList-ItemExpandOpened">
84-
<img class="TaskList-itemExpandControl" alt="unfold less" src="{{baseLink "/static/images/expand_more_black_24dp.svg"}}" />
85-
</span>
86-
</td>
87-
<td class="TaskList-itemCol TaskList-itemState">
88-
{{if $task.Error.Valid}}
89-
<img class="TaskList-itemStateIcon" alt="error" src="{{baseLink "/static/images/error_red_24dp.svg"}}" />
90-
{{else if $task.Finished}}
91-
<img class="TaskList-itemStateIcon" alt="finished" src="{{baseLink "/static/images/check_circle_green_24dp.svg"}}" />
92-
{{else}}
93-
<img class="TaskList-itemStateIcon" alt="pending" src="{{baseLink "/static/images/pending_yellow_24dp.svg"}}" />
94-
{{end}}
95-
</td>
96-
<td class="TaskList-itemCol TaskList-itemName">
97-
{{$task.Name}}
98-
</td>
99-
<td class="TaskList-itemCol TaskList-itemStarted">
100-
{{$task.CreatedAt.UTC.Format "Mon Jan _2 2006 15:04:05"}}
101-
</td>
102-
<td class="TaskList-itemCol TaskList-itemUpdated">
103-
{{$task.UpdatedAt.UTC.Format "Mon Jan _2 2006 15:04:05"}}
104-
</td>
105-
<td class="TaskList-itemCol TaskList-itemResult">
106-
{{$task.Result.String}}
107-
</td>
108-
<td class="TaskList-itemCol TaskList-itemAction">
109-
{{if $task.Error.Valid}}
110-
<div class="TaskList-retryTask">
111-
<form action="{{baseLink (printf "/workflows/%s/tasks/%s/retry" $workflow.ID $task.Name) }}" method="post">
112-
<input type="hidden" id="workflow.id" name="workflow.id" value="{{$workflow.ID}}" />
113-
<input class="Button Button--small" name="task.reset" type="submit" value="Retry" onclick="return this.form.reportValidity() && confirm('This will retry the task and clear workflow errors.\n\nReady to proceed?')" />
114-
</form>
115-
</div>
116-
{{end}}
117-
{{if and (not $task.Finished) (hasPrefix $task.Name "APPROVE-")}}
118-
<div class="TaskList-approveTask">
119-
<form action="{{baseLink (printf "/workflows/%s/tasks/%s/approve" $workflow.ID $task.Name) }}" method="post">
120-
<input type="hidden" id="workflow.id" name="workflow.id" value="{{$workflow.ID}}" />
121-
<input class="Button Button--small" name="task.approve" type="submit" value="Approve" onclick="return this.form.reportValidity() && confirm('This will mark the task approved and resume the workflow.\n\nReady to proceed?')" />
122-
</form>
123-
</div>
124-
{{end}}
125-
</td>
126-
</tr>
127-
<tr class="TaskList-itemLogsRow">
128-
<td class="TaskList-itemLogs" colspan="7">
129-
{{if $task.Error.Valid}}
130-
<div class="TaskList-itemLogLine TaskList-itemLogLineError">
131-
{{- $task.Error.Value -}}
132-
</div>
133-
{{end}}
134-
{{range $log := $.Logs $workflow.ID $task.Name}}
135-
<div class="TaskList-itemLogLine">
136-
{{- $log.CreatedAt.UTC.Format "2006/01/02 15:04:05"}} {{$log.Body -}}
137-
</div>
138-
{{end}}
139-
</td>
140-
</tr>
141-
</tbody>
142-
{{end}}
143-
</table>
67+
{{template "task_list" $detail}}
14468
</li>
14569
{{end}}
14670
</ul>

internal/relui/templates/layout.html

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
Use of this source code is governed by a BSD-style
44
license that can be found in the LICENSE file.
55
-->
6+
{{define "layout"}}
67
<!DOCTYPE html>
78
<html lang="en">
89
<title>{{.SiteHeader.Title}}</title>
@@ -16,7 +17,8 @@ <h1 class="Header-title">{{.SiteHeader.Title}}</h1>
1617
</div>
1718
</header>
1819
<main class="Site-content">
19-
{{template "content" .}}
20+
{{block "content" .}}{{end}}
2021
</main>
2122
</body>
2223
</html>
24+
{{end}}

internal/relui/templates/new_workflow.html

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
Use of this source code is governed by a BSD-style
44
license that can be found in the LICENSE file.
55
-->
6+
{{template "layout" .}}
7+
68
{{define "content"}}
79
<section class="NewWorkflow">
810
<h2>New Go Release</h2>
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
<!--
2+
Copyright 2022 The Go Authors. All rights reserved.
3+
Use of this source code is governed by a BSD-style
4+
license that can be found in the LICENSE file.
5+
-->
6+
{{define "task_list"}}
7+
{{$workflow := .Workflow}}
8+
<table class="TaskList">
9+
<thead>
10+
<tr class="TaskList-item TaskList-itemHeader">
11+
<th class="TaskList-itemHeaderCol TaskList-itemExpand"></th>
12+
<th class="TaskList-itemHeaderCol TaskList-itemState">State</th>
13+
<th class="TaskList-itemHeaderCol TaskList-itemName">Name</th>
14+
<th class="TaskList-itemHeaderCol TaskList-itemStarted">Started</th>
15+
<th class="TaskList-itemHeaderCol TaskList-itemUpdated">Updated</th>
16+
<th class="TaskList-itemHeaderCol TaskList-itemResult">Result</th>
17+
<th class="TaskList-itemHeaderCol TaskList-itemActions">Actions</th>
18+
</tr>
19+
</thead>
20+
{{range $task := .Tasks}}
21+
<tbody>
22+
<tr class="TaskList-item TaskList-itemSummary TaskList-expandableItem">
23+
<td class="TaskList-itemCol TaskList-itemExpand">
24+
<span class="TaskList-itemExpandClosed">
25+
<img class="TaskList-itemExpandControl" alt="unfold more" src="{{baseLink "/static/images/chevron_right_black_24dp.svg"}}" />
26+
</span>
27+
<span class="TaskList-ItemExpandOpened">
28+
<img class="TaskList-itemExpandControl" alt="unfold less" src="{{baseLink "/static/images/expand_more_black_24dp.svg"}}" />
29+
</span>
30+
</td>
31+
<td class="TaskList-itemCol TaskList-itemState">
32+
{{if $task.Error.Valid}}
33+
<img class="TaskList-itemStateIcon" alt="error" src="{{baseLink "/static/images/error_red_24dp.svg"}}" />
34+
{{else if $task.Finished}}
35+
<img
36+
class="TaskList-itemStateIcon"
37+
alt="finished"
38+
src="{{baseLink "/static/images/check_circle_green_24dp.svg"}}" />
39+
{{else}}
40+
<img
41+
class="TaskList-itemStateIcon"
42+
alt="pending"
43+
src="{{baseLink "/static/images/pending_yellow_24dp.svg"}}" />
44+
{{end}}
45+
</td>
46+
<td class="TaskList-itemCol TaskList-itemName">
47+
{{$task.Name}}
48+
</td>
49+
<td class="TaskList-itemCol TaskList-itemStarted">
50+
{{$task.CreatedAt.UTC.Format "Mon Jan _2 2006 15:04:05"}}
51+
</td>
52+
<td class="TaskList-itemCol TaskList-itemUpdated">
53+
{{$task.UpdatedAt.UTC.Format "Mon Jan _2 2006 15:04:05"}}
54+
</td>
55+
<td class="TaskList-itemCol TaskList-itemResult">
56+
{{$task.Result.String}}
57+
</td>
58+
<td class="TaskList-itemCol TaskList-itemAction">
59+
{{if $task.Error.Valid}}
60+
<div class="TaskList-retryTask">
61+
<form action="{{baseLink (printf "/workflows/%s/tasks/%s/retry" $workflow.ID $task.Name)}}" method="post">
62+
<input type="hidden" id="workflow.id" name="workflow.id" value="{{$workflow.ID}}" />
63+
<input class="Button Button--small" name="task.reset" type="submit" value="Retry" onclick="return this.form.reportValidity() && confirm('This will retry the task and clear workflow errors.\n\nReady to proceed?')" />
64+
</form>
65+
</div>
66+
{{end}}
67+
{{if and (not $task.Finished) (hasPrefix $task.Name "APPROVE-")}}
68+
<div class="TaskList-approveTask">
69+
<form action="{{baseLink (printf "/workflows/%s/tasks/%s/approve" $workflow.ID $task.Name)}}" method="post">
70+
<input type="hidden" id="workflow.id" name="workflow.id" value="{{$workflow.ID}}" />
71+
<input class="Button Button--small" name="task.approve" type="submit" value="Approve" onclick="return this.form.reportValidity() && confirm('This will mark the task approved and resume the workflow.\n\nReady to proceed?')" />
72+
</form>
73+
</div>
74+
{{end}}
75+
</td>
76+
</tr>
77+
<tr class="TaskList-itemLogsRow">
78+
<td class="TaskList-itemLogs" colspan="7">
79+
{{if $task.Error.Valid}}
80+
<div class="TaskList-itemLogLine TaskList-itemLogLineError">
81+
{{- $task.Error.Value -}}
82+
</div>
83+
{{end}}
84+
{{range $log := index $.TaskLogs $task.Name}}
85+
<div class="TaskList-itemLogLine">
86+
{{- $log.CreatedAt.UTC.Format "2006/01/02 15:04:05"}} {{$log.Body -}}
87+
</div>
88+
{{end}}
89+
</td>
90+
</tr>
91+
</tbody>
92+
{{end}}
93+
</table>
94+
{{end}}

internal/relui/web.go

Lines changed: 38 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ type Server struct {
5959
// mux used if baseURL is set
6060
bm *http.ServeMux
6161

62+
templates *template.Template
6263
homeTmpl *template.Template
6364
newWorkflowTmpl *template.Template
6465
}
@@ -79,9 +80,9 @@ func NewServer(p *pgxpool.Pool, w *Worker, baseURL *url.URL, header SiteHeader)
7980
"baseLink": s.BaseLink,
8081
"hasPrefix": strings.HasPrefix,
8182
}
82-
layout := template.Must(template.New("layout.html").Funcs(helpers).ParseFS(templates, "templates/layout.html"))
83-
s.homeTmpl = template.Must(template.Must(layout.Clone()).Funcs(helpers).ParseFS(templates, "templates/home.html"))
84-
s.newWorkflowTmpl = template.Must(template.Must(layout.Clone()).Funcs(helpers).ParseFS(templates, "templates/new_workflow.html"))
83+
s.templates = template.Must(template.New("").Funcs(helpers).ParseFS(templates, "templates/*.html"))
84+
s.homeTmpl = s.mustLookup("home.html")
85+
s.newWorkflowTmpl = s.mustLookup("new_workflow.html")
8586
s.m.POST("/workflows/:id/stop", s.stopWorkflowHandler)
8687
s.m.POST("/workflows/:id/tasks/:name/retry", s.retryTaskHandler)
8788
s.m.POST("/workflows/:id/tasks/:name/approve", s.approveTaskHandler)
@@ -98,6 +99,14 @@ func NewServer(p *pgxpool.Pool, w *Worker, baseURL *url.URL, header SiteHeader)
9899
return s
99100
}
100101

102+
func (s *Server) mustLookup(name string) *template.Template {
103+
t := template.Must(template.Must(s.templates.Clone()).ParseFS(templates, path.Join("templates", name))).Lookup(name)
104+
if t == nil {
105+
panic(fmt.Errorf("template %q not found", name))
106+
}
107+
return t
108+
}
109+
101110
func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
102111
if s.bm != nil {
103112
s.bm.ServeHTTP(w, r)
@@ -124,19 +133,18 @@ func (s *Server) BaseLink(target string) string {
124133
return u.String()
125134
}
126135

127-
type homeResponse struct {
128-
SiteHeader SiteHeader
129-
Workflows []db.Workflow
130-
WorkflowTasks map[uuid.UUID][]db.Task
131-
TaskLogs map[uuid.UUID]map[string][]db.TaskLog
136+
type workflowDetail struct {
137+
Workflow db.Workflow
138+
Tasks []db.Task
139+
// TaskLogs is a map of all logs for a db.Task, keyed on
140+
// (db.Task).Name
141+
TaskLogs map[string][]db.TaskLog
132142
}
133143

134-
func (h *homeResponse) Logs(workflow uuid.UUID, task string) []db.TaskLog {
135-
t := h.TaskLogs[workflow]
136-
if t == nil {
137-
return nil
138-
}
139-
return t[task]
144+
type homeResponse struct {
145+
SiteHeader SiteHeader
146+
WorkflowIDs []uuid.UUID
147+
WorkflowDetails map[uuid.UUID]*workflowDetail
140148
}
141149

142150
func (h *homeResponse) WorkflowParams(wf db.Workflow) map[string]string {
@@ -172,22 +180,31 @@ func (s *Server) buildHomeResponse(ctx context.Context) (*homeResponse, error) {
172180
if err != nil {
173181
return nil, err
174182
}
175-
wfTasks := make(map[uuid.UUID][]db.Task, len(ws))
183+
hr := &homeResponse{
184+
SiteHeader: s.header,
185+
WorkflowDetails: make(map[uuid.UUID]*workflowDetail),
186+
}
187+
for _, w := range ws {
188+
hr.WorkflowIDs = append(hr.WorkflowIDs, w.ID)
189+
hr.WorkflowDetails[w.ID] = &workflowDetail{Workflow: w}
190+
}
176191
for _, t := range tasks {
177-
wfTasks[t.WorkflowID] = append(wfTasks[t.WorkflowID], t)
192+
wd := hr.WorkflowDetails[t.WorkflowID]
193+
wd.Tasks = append(hr.WorkflowDetails[t.WorkflowID].Tasks, t)
194+
wd.TaskLogs = make(map[string][]db.TaskLog)
178195
}
179196
tlogs, err := q.TaskLogs(ctx)
180197
if err != nil {
181198
return nil, err
182199
}
183-
wftlogs := make(map[uuid.UUID]map[string][]db.TaskLog)
184200
for _, l := range tlogs {
185-
if wftlogs[l.WorkflowID] == nil {
186-
wftlogs[l.WorkflowID] = make(map[string][]db.TaskLog)
201+
wd := hr.WorkflowDetails[l.WorkflowID]
202+
if wd.TaskLogs == nil {
203+
wd.TaskLogs = make(map[string][]db.TaskLog)
187204
}
188-
wftlogs[l.WorkflowID][l.TaskName] = append(wftlogs[l.WorkflowID][l.TaskName], l)
205+
wd.TaskLogs[l.TaskName] = append(wd.TaskLogs[l.TaskName], l)
189206
}
190-
return &homeResponse{SiteHeader: s.header, Workflows: ws, WorkflowTasks: wfTasks, TaskLogs: wftlogs}, nil
207+
return hr, nil
191208
}
192209

193210
type newWorkflowResponse struct {

0 commit comments

Comments
 (0)